<commit_before>use std::cmp;
use std::fmt;
use std::io;
use std::ops::Deref;
use std::ops::DerefMut;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;

use bytes::BufMut;
use bytes::Bytes;
use bytes::BytesMut;
use futures::future;
use futures::Future;
use futures::Stream;
use tokio::io::AsyncRead;
use tokio::io::ReadBuf;

use crate::solicit_async::TryFutureBox;
use crate::solicit_async::TryStreamBox;
use crate::DataOrTrailers;
use crate::Headers;

pub trait StreamAfterHeaders: fmt::Debug + Unpin + Send + 'static {
    /// Fetch the next message without increasing the window size.
    ///
    /// This is lower-level operation, might not be needed to be used.
    fn poll_next_no_auto(
        &mut self,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<DataOrTrailers>>>;

    /// `Stream`-like operation.
    fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<DataOrTrailers>>>;

    /// Poll `DATA` frame from the stream.
    ///
    /// Note when this operation returns `None`, trailers can still be fetched
    /// with [`poll_trailers`].
    fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Bytes>>>;

    /// Poll trailing `HEADERS` frame from the stream.
    ///
    /// This operation returns `None` if not all `DATA` frames fetched yet.
    fn poll_trailers(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Headers>>>;

    /// Current effective Window size known to the caller. This is a sum of:
    ///
    /// * window size known to the peer
    /// * all `DATA` messages traveling in the network
    /// * all `DATA` frame not yet fetched from this stream
    fn in_window_size(&self) -> u32;

    /// Explicitly request to increase Window size.
    ///
    /// Note this operation might be expensive, because each invocation sends a `WINDOW_UPDATE`
    /// frame. You probably might want to use default or update the default with
    /// [`set_auto_in_window_size`].
    fn inc_in_window(&mut self, delta: u32) -> crate::Result<()>;

    /// Window will be increased each time current window size drops below the half
    /// of given value when `poll_next` is used.
    fn set_auto_in_window_size(&mut self, window_size: u32) -> crate::Result<()>;

    // Utilities

    /// Fetch next frame.
    fn next<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = crate::Result<Option<DataOrTrailers>>> + 'a>> {
        Box::pin(future::poll_fn(move |cx| self.poll_next(cx)?.map(Ok)))
    }

    /// Fetch next `DATA`.
    fn next_data<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = crate::Result<Option<Bytes>>> + Send + 'a>> {
        Box::pin(future::poll_fn(move |cx| self.poll_data(cx)?.map(Ok)))
    }

    /// Fetch trailers. This returns `None` if not all `DATA` frames processed.
    fn next_trailers<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = crate::Result<Option<Headers>>> + 'a>> {
        Box::pin(future::poll_fn(move |cx| self.poll_trailers(cx)?.map(Ok)))
    }

    fn into_read(self) -> Pin<Box<dyn AsyncRead + Send + 'static>>
    where
        Self: Sized,
    {
        Box::pin(AsRead {
            stream: self,
            rem: Bytes::new(),
        })
    }

    fn into_stream(self) -> TryStreamBox<DataOrTrailers>
    where
        Self: Sized,
    {
        Box::pin(AsStream(self))
    }

    fn filter_data(self) -> TryStreamBox<Bytes>
    where
        Self: Sized,
    {
        Box::pin(DataStream(self))
    }

    fn collect_data(mut self) -> TryFutureBox<Bytes>
    where
        Self: Sized,
    {
        Box::pin(async move {
            let mut r = BytesMut::new();
            loop {
                match self.next_data().await? {
                    None => return Ok(r.freeze()),
                    Some(b) => {
                        // TODO: figure out how to efficiently extend from Bytes
                        r.put_slice(&b)
                    }
                }
            }
        })
    }
}

struct AsStream<S: StreamAfterHeaders>(S);

impl<S: StreamAfterHeaders> Stream for AsStream<S> {
    type Item = crate::Result<DataOrTrailers>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.get_mut().0).poll_next(cx)
    }
}

struct DataStream<S: StreamAfterHeaders>(S);

impl<S: StreamAfterHeaders> Stream for DataStream<S> {
    type Item = crate::Result<Bytes>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.get_mut().0).poll_data(cx)
    }
}

pub struct AsRead<S: StreamAfterHeaders> {
    stream: S,
    rem: Bytes,
}

impl<S: StreamAfterHeaders> AsyncRead for AsRead<S> {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf,
    ) -> Poll<io::Result<()>> {
        // TODO: untested
        let me = self.get_mut();

        loop {
            if !me.rem.is_empty() {
                let min = cmp::min(me.rem.len(), buf.remaining());
                buf.put_slice(&me.rem.split_to(min));
                return Poll::Ready(Ok(()));
            }

            me.rem = match Pin::new(&mut me.stream)
                .poll_data(cx)
                .map_err(Into::<io::Error>::into)?
            {
                Poll::Ready(None) => return Poll::Ready(Ok(())),
                Poll::Ready(Some(bytes)) => bytes,
                Poll::Pending => return Poll::Pending,
            }
        }
    }
}

pub type StreamAfterHeadersBox = Pin<Box<dyn StreamAfterHeaders>>;

#[derive(Debug)]
pub(crate) struct HttpStreamAfterHeadersEmpty;

impl StreamAfterHeaders for HttpStreamAfterHeadersEmpty {
    fn poll_next(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        Poll::Ready(None)
    }

    fn in_window_size(&self) -> u32 {
        0
    }

    fn inc_in_window(&mut self, _delta: u32) -> crate::Result<()> {
        Ok(())
    }

    fn set_auto_in_window_size(&mut self, _window_size: u32) -> crate::Result<()> {
        Ok(())
    }

    fn poll_next_no_auto(
        &mut self,
        _cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        Poll::Ready(None)
    }

    fn poll_data(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<Bytes>>> {
        Poll::Ready(None)
    }

    fn poll_trailers(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<Headers>>> {
        Poll::Ready(None)
    }
}

impl StreamAfterHeaders for Pin<Box<dyn StreamAfterHeaders>> {
    fn poll_next(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        self.deref_mut().poll_next(_cx)
    }

    fn in_window_size(&self) -> u32 {
        self.deref().in_window_size()
    }

    fn inc_in_window(&mut self, delta: u32) -> crate::Result<()> {
        self.deref_mut().inc_in_window(delta)
    }

    fn set_auto_in_window_size(&mut self, window_size: u32) -> crate::Result<()> {
        self.deref_mut().set_auto_in_window_size(window_size)
    }

    fn poll_next_no_auto(
        &mut self,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        self.deref_mut().poll_next_no_auto(cx)
    }

    fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Bytes>>> {
        self.deref_mut().poll_data(cx)
    }

    fn poll_trailers(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Headers>>> {
        self.deref_mut().poll_trailers(cx)
    }
}
<commit_msg>Compat with stable rust<commit_after>use std::cmp;
use std::fmt;
use std::io;
use std::ops::Deref;
use std::ops::DerefMut;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;

use bytes::BufMut;
use bytes::Bytes;
use bytes::BytesMut;
use futures::future;
use futures::Future;
use futures::Stream;
use tokio::io::AsyncRead;
use tokio::io::ReadBuf;

use crate::solicit_async::TryFutureBox;
use crate::solicit_async::TryStreamBox;
use crate::DataOrTrailers;
use crate::Headers;

pub trait StreamAfterHeaders: fmt::Debug + Unpin + Send + 'static {
    /// Fetch the next message without increasing the window size.
    ///
    /// This is lower-level operation, might not be needed to be used.
    fn poll_next_no_auto(
        &mut self,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<DataOrTrailers>>>;

    /// `Stream`-like operation.
    fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<DataOrTrailers>>>;

    /// Poll `DATA` frame from the stream.
    ///
    /// Note when this operation returns `None`, trailers can still be fetched
    /// with [`poll_trailers`].
    fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Bytes>>>;

    /// Poll trailing `HEADERS` frame from the stream.
    ///
    /// This operation returns `None` if not all `DATA` frames fetched yet.
    fn poll_trailers(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Headers>>>;

    /// Current effective Window size known to the caller. This is a sum of:
    ///
    /// * window size known to the peer
    /// * all `DATA` messages traveling in the network
    /// * all `DATA` frame not yet fetched from this stream
    fn in_window_size(&self) -> u32;

    /// Explicitly request to increase Window size.
    ///
    /// Note this operation might be expensive, because each invocation sends a `WINDOW_UPDATE`
    /// frame. You probably might want to use default or update the default with
    /// [`set_auto_in_window_size`].
    fn inc_in_window(&mut self, delta: u32) -> crate::Result<()>;

    /// Window will be increased each time current window size drops below the half
    /// of given value when `poll_next` is used.
    fn set_auto_in_window_size(&mut self, window_size: u32) -> crate::Result<()>;

    // Utilities

    /// Fetch next frame.
    fn next<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = crate::Result<Option<DataOrTrailers>>> + 'a>> {
        Box::pin(future::poll_fn(move |cx| self.poll_next(cx)?.map(Ok)))
    }

    /// Fetch next `DATA`.
    fn next_data<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = crate::Result<Option<Bytes>>> + Send + 'a>> {
        Box::pin(future::poll_fn(move |cx| self.poll_data(cx)?.map(Ok)))
    }

    /// Fetch trailers. This returns `None` if not all `DATA` frames processed.
    fn next_trailers<'a>(
        &'a mut self,
    ) -> Pin<Box<dyn Future<Output = crate::Result<Option<Headers>>> + 'a>> {
        Box::pin(future::poll_fn(move |cx| self.poll_trailers(cx)?.map(Ok)))
    }

    fn into_read(self) -> Pin<Box<dyn AsyncRead + Send + 'static>>
    where
        Self: Sized,
    {
        Box::pin(AsRead {
            stream: self,
            rem: Bytes::new(),
        })
    }

    fn into_stream(self) -> TryStreamBox<DataOrTrailers>
    where
        Self: Sized,
    {
        Box::pin(AsStream(self))
    }

    fn filter_data(self) -> TryStreamBox<Bytes>
    where
        Self: Sized,
    {
        Box::pin(DataStream(self))
    }

    fn collect_data(mut self) -> TryFutureBox<Bytes>
    where
        Self: Sized,
    {
        Box::pin(async move {
            let mut r = BytesMut::new();
            loop {
                match self.next_data().await? {
                    None => return Ok(r.freeze()),
                    Some(b) => {
                        // TODO: figure out how to efficiently extend from Bytes
                        r.put_slice(&b)
                    }
                }
            }
        })
    }
}

struct AsStream<S: StreamAfterHeaders>(S);

impl<S: StreamAfterHeaders> Stream for AsStream<S> {
    type Item = crate::Result<DataOrTrailers>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.get_mut().0).poll_next(cx)
    }
}

struct DataStream<S: StreamAfterHeaders>(S);

impl<S: StreamAfterHeaders> Stream for DataStream<S> {
    type Item = crate::Result<Bytes>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Pin::new(&mut self.get_mut().0).poll_data(cx)
    }
}

pub struct AsRead<S: StreamAfterHeaders> {
    stream: S,
    rem: Bytes,
}

impl<S: StreamAfterHeaders> AsyncRead for AsRead<S> {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf,
    ) -> Poll<io::Result<()>> {
        // TODO: untested
        let me = self.get_mut();

        loop {
            if !me.rem.is_empty() {
                let min = cmp::min(me.rem.len(), buf.remaining());
                buf.put_slice(&me.rem.split_to(min));
                return Poll::Ready(Ok(()));
            }

            me.rem = match Pin::new(&mut me.stream).poll_data(cx) {
                Poll::Ready(Some(Err(e))) => return Poll::Ready(Err(e.into())),
                Poll::Ready(None) => return Poll::Ready(Ok(())),
                Poll::Ready(Some(Ok(bytes))) => bytes,
                Poll::Pending => return Poll::Pending,
            }
        }
    }
}

pub type StreamAfterHeadersBox = Pin<Box<dyn StreamAfterHeaders>>;

#[derive(Debug)]
pub(crate) struct HttpStreamAfterHeadersEmpty;

impl StreamAfterHeaders for HttpStreamAfterHeadersEmpty {
    fn poll_next(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        Poll::Ready(None)
    }

    fn in_window_size(&self) -> u32 {
        0
    }

    fn inc_in_window(&mut self, _delta: u32) -> crate::Result<()> {
        Ok(())
    }

    fn set_auto_in_window_size(&mut self, _window_size: u32) -> crate::Result<()> {
        Ok(())
    }

    fn poll_next_no_auto(
        &mut self,
        _cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        Poll::Ready(None)
    }

    fn poll_data(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<Bytes>>> {
        Poll::Ready(None)
    }

    fn poll_trailers(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<Headers>>> {
        Poll::Ready(None)
    }
}

impl StreamAfterHeaders for Pin<Box<dyn StreamAfterHeaders>> {
    fn poll_next(&mut self, _cx: &mut Context<'_>) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        self.deref_mut().poll_next(_cx)
    }

    fn in_window_size(&self) -> u32 {
        self.deref().in_window_size()
    }

    fn inc_in_window(&mut self, delta: u32) -> crate::Result<()> {
        self.deref_mut().inc_in_window(delta)
    }

    fn set_auto_in_window_size(&mut self, window_size: u32) -> crate::Result<()> {
        self.deref_mut().set_auto_in_window_size(window_size)
    }

    fn poll_next_no_auto(
        &mut self,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<DataOrTrailers>>> {
        self.deref_mut().poll_next_no_auto(cx)
    }

    fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Bytes>>> {
        self.deref_mut().poll_data(cx)
    }

    fn poll_trailers(&mut self, cx: &mut Context<'_>) -> Poll<Option<crate::Result<Headers>>> {
        self.deref_mut().poll_trailers(cx)
    }
}
<|endoftext|>


<commit_before>//! This module takes a (parsed) definition of `macro_rules` invocation, a
//! `tt::TokenTree` representing an argument of macro invocation, and produces a
//! `tt::TokenTree` for the result of the expansion.

mod matcher;
mod transcriber;

use ra_syntax::SmolStr;
use rustc_hash::FxHashMap;

use crate::{ExpandError, ExpandResult};

pub(crate) fn expand(rules: &crate::MacroRules, input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
    expand_rules(&rules.rules, input)
}

fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
    let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
    let mut err = Some(ExpandError::NoMatchingRule);
    for rule in rules {
        let ExpandResult(new_match, bindings_err) = matcher::match_(&rule.lhs, input);
        if bindings_err.is_none() {
            // if we find a rule that applies without errors, we're done
            let ExpandResult(res, transcribe_err) =
                transcriber::transcribe(&rule.rhs, &new_match.bindings);
            if transcribe_err.is_none() {
                return ExpandResult::ok(res);
            }
        }
        // use the rule if we matched more tokens, or had fewer patterns left
        if let Some((prev_match, _)) = &match_ {
            if new_match.unmatched_tokens < prev_match.unmatched_tokens
                || new_match.unmatched_tokens == prev_match.unmatched_tokens
                    && new_match.unmatched_patterns < prev_match.unmatched_patterns
                || err.is_some() && bindings_err.is_none()
            {
                match_ = Some((new_match, rule));
                err = bindings_err;
            }
        } else {
            match_ = Some((new_match, rule));
            err = bindings_err;
        }
    }
    if let Some((match_, rule)) = match_ {
        // if we got here, there was no match without errors
        let ExpandResult(result, transcribe_err) =
            transcriber::transcribe(&rule.rhs, &match_.bindings);
        ExpandResult(result, err.or(transcribe_err))
    } else {
        ExpandResult(tt::Subtree::default(), err)
    }
}

/// The actual algorithm for expansion is not too hard, but is pretty tricky.
/// `Bindings` structure is the key to understanding what we are doing here.
///
/// On the high level, it stores mapping from meta variables to the bits of
/// syntax it should be substituted with. For example, if `$e:expr` is matched
/// with `1 + 1` by macro_rules, the `Binding` will store `$e -> 1 + 1`.
///
/// The tricky bit is dealing with repetitions (`$()*`). Consider this example:
///
/// ```not_rust
/// macro_rules! foo {
///     ($($ i:ident $($ e:expr),*);*) => {
///         $(fn $ i() { $($ e);*; })*
///     }
/// }
/// foo! { foo 1,2,3; bar 4,5,6 }
/// ```
///
/// Here, the `$i` meta variable is matched first with `foo` and then with
/// `bar`, and `$e` is matched in turn with `1`, `2`, `3`, `4`, `5`, `6`.
///
/// To represent such "multi-mappings", we use a recursive structures: we map
/// variables not to values, but to *lists* of values or other lists (that is,
/// to the trees).
///
/// For the above example, the bindings would store
///
/// ```not_rust
/// i -> [foo, bar]
/// e -> [[1, 2, 3], [4, 5, 6]]
/// ```
///
/// We construct `Bindings` in the `match_lhs`. The interesting case is
/// `TokenTree::Repeat`, where we use `push_nested` to create the desired
/// nesting structure.
///
/// The other side of the puzzle is `expand_subtree`, where we use the bindings
/// to substitute meta variables in the output template. When expanding, we
/// maintain a `nesting` stack of indices which tells us which occurrence from
/// the `Bindings` we should take. We push to the stack when we enter a
/// repetition.
///
/// In other words, `Bindings` is a *multi* mapping from `SmolStr` to
/// `tt::TokenTree`, where the index to select a particular `TokenTree` among
/// many is not a plain `usize`, but an `&[usize]`.
#[derive(Debug, Default)]
struct Bindings {
    inner: FxHashMap<SmolStr, Binding>,
}

#[derive(Debug)]
enum Binding {
    Fragment(Fragment),
    Nested(Vec<Binding>),
    Empty,
}

#[derive(Debug, Clone)]
enum Fragment {
    /// token fragments are just copy-pasted into the output
    Tokens(tt::TokenTree),
    /// Ast fragments are inserted with fake delimiters, so as to make things
    /// like `$i * 2` where `$i = 1 + 1` work as expectd.
    Ast(tt::TokenTree),
}

#[cfg(test)]
mod tests {
    use ra_syntax::{ast, AstNode};

    use super::*;
    use crate::ast_to_token_tree;

    #[test]
    fn test_expand_rule() {
        assert_err(
            "($($i:ident);*) => ($i)",
            "foo!{a}",
            ExpandError::BindingError(String::from(
                "expected simple binding, found nested binding `i`",
            )),
        );

        // FIXME:
        // Add an err test case for ($($i:ident)) => ($())
    }

    fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) {
        assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation).1, Some(err));
    }

    fn format_macro(macro_body: &str) -> String {
        format!(
            "
        macro_rules! foo {{
            {}
        }}
",
            macro_body
        )
    }

    fn create_rules(macro_definition: &str) -> crate::MacroRules {
        let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap();
        let macro_definition =
            source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();

        let (definition_tt, _) =
            ast_to_token_tree(&macro_definition.token_tree().unwrap()).unwrap();
        crate::MacroRules::parse(&definition_tt).unwrap()
    }

    fn expand_first(rules: &crate::MacroRules, invocation: &str) -> ExpandResult<tt::Subtree> {
        let source_file = ast::SourceFile::parse(invocation).ok().unwrap();
        let macro_invocation =
            source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();

        let (invocation_tt, _) =
            ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap();

        let expanded = expand_rules(&rules.rules, &invocation_tt);
        expanded
    }
}
<commit_msg>Some cleanup<commit_after>//! This module takes a (parsed) definition of `macro_rules` invocation, a
//! `tt::TokenTree` representing an argument of macro invocation, and produces a
//! `tt::TokenTree` for the result of the expansion.

mod matcher;
mod transcriber;

use ra_syntax::SmolStr;
use rustc_hash::FxHashMap;

use crate::{ExpandError, ExpandResult};

pub(crate) fn expand(rules: &crate::MacroRules, input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
    expand_rules(&rules.rules, input)
}

fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
    let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
    let mut err = Some(ExpandError::NoMatchingRule);
    for rule in rules {
        let ExpandResult(new_match, bindings_err) = matcher::match_(&rule.lhs, input);
        if bindings_err.is_none() {
            // If we find a rule that applies without errors, we're done.
            // Unconditionally returning the transcription here makes the
            // `test_repeat_bad_var` test fail.
            let ExpandResult(res, transcribe_err) =
                transcriber::transcribe(&rule.rhs, &new_match.bindings);
            if transcribe_err.is_none() {
                return ExpandResult::ok(res);
            }
        }
        // Use the rule if we matched more tokens, or had fewer patterns left,
        // or had no error
        if let Some((prev_match, _)) = &match_ {
            if (new_match.unmatched_tokens, new_match.unmatched_patterns)
                < (prev_match.unmatched_tokens, prev_match.unmatched_patterns)
                || err.is_some() && bindings_err.is_none()
            {
                match_ = Some((new_match, rule));
                err = bindings_err;
            }
        } else {
            match_ = Some((new_match, rule));
            err = bindings_err;
        }
    }
    if let Some((match_, rule)) = match_ {
        // if we got here, there was no match without errors
        let ExpandResult(result, transcribe_err) =
            transcriber::transcribe(&rule.rhs, &match_.bindings);
        ExpandResult(result, err.or(transcribe_err))
    } else {
        ExpandResult(tt::Subtree::default(), err)
    }
}

/// The actual algorithm for expansion is not too hard, but is pretty tricky.
/// `Bindings` structure is the key to understanding what we are doing here.
///
/// On the high level, it stores mapping from meta variables to the bits of
/// syntax it should be substituted with. For example, if `$e:expr` is matched
/// with `1 + 1` by macro_rules, the `Binding` will store `$e -> 1 + 1`.
///
/// The tricky bit is dealing with repetitions (`$()*`). Consider this example:
///
/// ```not_rust
/// macro_rules! foo {
///     ($($ i:ident $($ e:expr),*);*) => {
///         $(fn $ i() { $($ e);*; })*
///     }
/// }
/// foo! { foo 1,2,3; bar 4,5,6 }
/// ```
///
/// Here, the `$i` meta variable is matched first with `foo` and then with
/// `bar`, and `$e` is matched in turn with `1`, `2`, `3`, `4`, `5`, `6`.
///
/// To represent such "multi-mappings", we use a recursive structures: we map
/// variables not to values, but to *lists* of values or other lists (that is,
/// to the trees).
///
/// For the above example, the bindings would store
///
/// ```not_rust
/// i -> [foo, bar]
/// e -> [[1, 2, 3], [4, 5, 6]]
/// ```
///
/// We construct `Bindings` in the `match_lhs`. The interesting case is
/// `TokenTree::Repeat`, where we use `push_nested` to create the desired
/// nesting structure.
///
/// The other side of the puzzle is `expand_subtree`, where we use the bindings
/// to substitute meta variables in the output template. When expanding, we
/// maintain a `nesting` stack of indices which tells us which occurrence from
/// the `Bindings` we should take. We push to the stack when we enter a
/// repetition.
///
/// In other words, `Bindings` is a *multi* mapping from `SmolStr` to
/// `tt::TokenTree`, where the index to select a particular `TokenTree` among
/// many is not a plain `usize`, but an `&[usize]`.
#[derive(Debug, Default)]
struct Bindings {
    inner: FxHashMap<SmolStr, Binding>,
}

#[derive(Debug)]
enum Binding {
    Fragment(Fragment),
    Nested(Vec<Binding>),
    Empty,
}

#[derive(Debug, Clone)]
enum Fragment {
    /// token fragments are just copy-pasted into the output
    Tokens(tt::TokenTree),
    /// Ast fragments are inserted with fake delimiters, so as to make things
    /// like `$i * 2` where `$i = 1 + 1` work as expectd.
    Ast(tt::TokenTree),
}

#[cfg(test)]
mod tests {
    use ra_syntax::{ast, AstNode};

    use super::*;
    use crate::ast_to_token_tree;

    #[test]
    fn test_expand_rule() {
        assert_err(
            "($($i:ident);*) => ($i)",
            "foo!{a}",
            ExpandError::BindingError(String::from(
                "expected simple binding, found nested binding `i`",
            )),
        );

        // FIXME:
        // Add an err test case for ($($i:ident)) => ($())
    }

    fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) {
        assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation).1, Some(err));
    }

    fn format_macro(macro_body: &str) -> String {
        format!(
            "
        macro_rules! foo {{
            {}
        }}
",
            macro_body
        )
    }

    fn create_rules(macro_definition: &str) -> crate::MacroRules {
        let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap();
        let macro_definition =
            source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();

        let (definition_tt, _) =
            ast_to_token_tree(&macro_definition.token_tree().unwrap()).unwrap();
        crate::MacroRules::parse(&definition_tt).unwrap()
    }

    fn expand_first(rules: &crate::MacroRules, invocation: &str) -> ExpandResult<tt::Subtree> {
        let source_file = ast::SourceFile::parse(invocation).ok().unwrap();
        let macro_invocation =
            source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();

        let (invocation_tt, _) =
            ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap();

        let expanded = expand_rules(&rules.rules, &invocation_tt);
        expanded
    }
}
<|endoftext|>


<commit_before>//! Various extension methods to ast Expr Nodes, which are hard to code-generate.
//!
//! These methods should only do simple, shallow tasks related to the syntax of the node itself.

use crate::{
    ast::{
        self,
        operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
        support, AstChildren, AstNode,
    },
    AstToken,
    SyntaxKind::*,
    SyntaxNode, SyntaxToken, T,
};

impl ast::HasAttrs for ast::Expr {}

impl ast::Expr {
    pub fn is_block_like(&self) -> bool {
        matches!(
            self,
            ast::Expr::IfExpr(_)
                | ast::Expr::LoopExpr(_)
                | ast::Expr::ForExpr(_)
                | ast::Expr::WhileExpr(_)
                | ast::Expr::BlockExpr(_)
                | ast::Expr::MatchExpr(_)
        )
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ElseBranch {
    Block(ast::BlockExpr),
    IfExpr(ast::IfExpr),
}

impl From<ast::BlockExpr> for ElseBranch {
    fn from(block_expr: ast::BlockExpr) -> Self {
        Self::Block(block_expr)
    }
}

impl From<ast::IfExpr> for ElseBranch {
    fn from(if_expr: ast::IfExpr) -> Self {
        Self::IfExpr(if_expr)
    }
}

impl ast::IfExpr {
    pub fn then_branch(&self) -> Option<ast::BlockExpr> {
        self.blocks().next()
    }

    pub fn else_branch(&self) -> Option<ElseBranch> {
        let res = match self.blocks().nth(1) {
            Some(block) => ElseBranch::Block(block),
            None => {
                let elif: ast::IfExpr = support::child(self.syntax())?;
                ElseBranch::IfExpr(elif)
            }
        };
        Some(res)
    }

    pub fn blocks(&self) -> AstChildren<ast::BlockExpr> {
        support::children(self.syntax())
    }
}

impl ast::PrefixExpr {
    pub fn op_kind(&self) -> Option<UnaryOp> {
        let res = match self.op_token()?.kind() {
            T![*] => UnaryOp::Deref,
            T![!] => UnaryOp::Not,
            T![-] => UnaryOp::Neg,
            _ => return None,
        };
        Some(res)
    }

    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.syntax().first_child_or_token()?.into_token()
    }
}

impl ast::BinExpr {
    pub fn op_details(&self) -> Option<(SyntaxToken, BinaryOp)> {
        self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {
            #[rustfmt::skip]
            let bin_op = match c.kind() {
                T![||] => BinaryOp::LogicOp(LogicOp::Or),
                T![&&] => BinaryOp::LogicOp(LogicOp::And),

                T![==] => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
                T![!=] => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
                T![<=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less,    strict: false }),
                T![>=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false }),
                T![<]  => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less,    strict: true }),
                T![>]  => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true }),

                T![+]  => BinaryOp::ArithOp(ArithOp::Add),
                T![*]  => BinaryOp::ArithOp(ArithOp::Mul),
                T![-]  => BinaryOp::ArithOp(ArithOp::Sub),
                T![/]  => BinaryOp::ArithOp(ArithOp::Div),
                T![%]  => BinaryOp::ArithOp(ArithOp::Rem),
                T![<<] => BinaryOp::ArithOp(ArithOp::Shl),
                T![>>] => BinaryOp::ArithOp(ArithOp::Shr),
                T![^]  => BinaryOp::ArithOp(ArithOp::BitXor),
                T![|]  => BinaryOp::ArithOp(ArithOp::BitOr),
                T![&]  => BinaryOp::ArithOp(ArithOp::BitAnd),

                T![=]   => BinaryOp::Assignment { op: None },
                T![+=]  => BinaryOp::Assignment { op: Some(ArithOp::Add) },
                T![*=]  => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
                T![-=]  => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
                T![/=]  => BinaryOp::Assignment { op: Some(ArithOp::Div) },
                T![%=]  => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
                T![<<=] => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
                T![>>=] => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
                T![^=]  => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
                T![|=]  => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
                T![&=]  => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },

                _ => return None,
            };
            Some((c, bin_op))
        })
    }

    pub fn op_kind(&self) -> Option<BinaryOp> {
        self.op_details().map(|t| t.1)
    }

    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.op_details().map(|t| t.0)
    }

    pub fn lhs(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).next()
    }

    pub fn rhs(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).nth(1)
    }

    pub fn sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>) {
        let mut children = support::children(self.syntax());
        let first = children.next();
        let second = children.next();
        (first, second)
    }
}

impl ast::RangeExpr {
    fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> {
        self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| {
            let token = child.into_token()?;
            let bin_op = match token.kind() {
                T![..] => RangeOp::Exclusive,
                T![..=] => RangeOp::Inclusive,
                _ => return None,
            };
            Some((ix, token, bin_op))
        })
    }

    pub fn op_kind(&self) -> Option<RangeOp> {
        self.op_details().map(|t| t.2)
    }

    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.op_details().map(|t| t.1)
    }

    pub fn start(&self) -> Option<ast::Expr> {
        let op_ix = self.op_details()?.0;
        self.syntax()
            .children_with_tokens()
            .take(op_ix)
            .find_map(|it| ast::Expr::cast(it.into_node()?))
    }

    pub fn end(&self) -> Option<ast::Expr> {
        let op_ix = self.op_details()?.0;
        self.syntax()
            .children_with_tokens()
            .skip(op_ix + 1)
            .find_map(|it| ast::Expr::cast(it.into_node()?))
    }
}

impl ast::IndexExpr {
    pub fn base(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).next()
    }
    pub fn index(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).nth(1)
    }
}

pub enum ArrayExprKind {
    Repeat { initializer: Option<ast::Expr>, repeat: Option<ast::Expr> },
    ElementList(AstChildren<ast::Expr>),
}

impl ast::ArrayExpr {
    pub fn kind(&self) -> ArrayExprKind {
        if self.is_repeat() {
            ArrayExprKind::Repeat {
                initializer: support::children(self.syntax()).next(),
                repeat: support::children(self.syntax()).nth(1),
            }
        } else {
            ArrayExprKind::ElementList(support::children(self.syntax()))
        }
    }

    fn is_repeat(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![;])
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum LiteralKind {
    String(ast::String),
    ByteString(ast::ByteString),
    IntNumber(ast::IntNumber),
    FloatNumber(ast::FloatNumber),
    Char,
    Byte,
    Bool(bool),
}

impl ast::Literal {
    pub fn token(&self) -> SyntaxToken {
        self.syntax()
            .children_with_tokens()
            .find(|e| e.kind() != ATTR && !e.kind().is_trivia())
            .and_then(|e| e.into_token())
            .unwrap()
    }

    pub fn kind(&self) -> LiteralKind {
        let token = self.token();

        if let Some(t) = ast::IntNumber::cast(token.clone()) {
            return LiteralKind::IntNumber(t);
        }
        if let Some(t) = ast::FloatNumber::cast(token.clone()) {
            return LiteralKind::FloatNumber(t);
        }
        if let Some(t) = ast::String::cast(token.clone()) {
            return LiteralKind::String(t);
        }
        if let Some(t) = ast::ByteString::cast(token.clone()) {
            return LiteralKind::ByteString(t);
        }

        match token.kind() {
            T![true] => LiteralKind::Bool(true),
            T![false] => LiteralKind::Bool(false),
            CHAR => LiteralKind::Char,
            BYTE => LiteralKind::Byte,
            _ => unreachable!(),
        }
    }
}

pub enum BlockModifier {
    Async(SyntaxToken),
    Unsafe(SyntaxToken),
    Try(SyntaxToken),
    Const(SyntaxToken),
    Label(ast::Label),
}

impl ast::BlockExpr {
    pub fn modifier(&self) -> Option<BlockModifier> {
        self.async_token()
            .map(BlockModifier::Async)
            .or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
            .or_else(|| self.try_token().map(BlockModifier::Try))
            .or_else(|| self.const_token().map(BlockModifier::Const))
            .or_else(|| self.label().map(BlockModifier::Label))
    }
    /// false if the block is an intrinsic part of the syntax and can't be
    /// replaced with arbitrary expression.
    ///
    /// ```not_rust
    /// fn foo() { not_stand_alone }
    /// const FOO: () = { stand_alone };
    /// ```
    pub fn is_standalone(&self) -> bool {
        let parent = match self.syntax().parent() {
            Some(it) => it,
            None => return true,
        };
        !matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR)
    }
}

#[test]
fn test_literal_with_attr() {
    let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
    let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap();
    assert_eq!(lit.token().text(), r#""Hello""#);
}

impl ast::RecordExprField {
    pub fn parent_record_lit(&self) -> ast::RecordExpr {
        self.syntax().ancestors().find_map(ast::RecordExpr::cast).unwrap()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CallableExpr {
    Call(ast::CallExpr),
    MethodCall(ast::MethodCallExpr),
}

impl ast::HasAttrs for CallableExpr {}
impl ast::HasArgList for CallableExpr {}

impl AstNode for CallableExpr {
    fn can_cast(kind: parser::SyntaxKind) -> bool
    where
        Self: Sized,
    {
        ast::CallExpr::can_cast(kind) || ast::MethodCallExpr::can_cast(kind)
    }

    fn cast(syntax: SyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if let Some(it) = ast::CallExpr::cast(syntax.clone()) {
            Some(Self::Call(it))
        } else if let Some(it) = ast::MethodCallExpr::cast(syntax) {
            Some(Self::MethodCall(it))
        } else {
            None
        }
    }

    fn syntax(&self) -> &SyntaxNode {
        match self {
            Self::Call(it) => it.syntax(),
            Self::MethodCall(it) => it.syntax(),
        }
    }
}
<commit_msg>Fix `if` in #11561<commit_after>//! Various extension methods to ast Expr Nodes, which are hard to code-generate.
//!
//! These methods should only do simple, shallow tasks related to the syntax of the node itself.

use crate::{
    ast::{
        self,
        operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
        support, AstChildren, AstNode,
    },
    AstToken,
    SyntaxKind::*,
    SyntaxNode, SyntaxToken, T,
};

impl ast::HasAttrs for ast::Expr {}

impl ast::Expr {
    pub fn is_block_like(&self) -> bool {
        matches!(
            self,
            ast::Expr::IfExpr(_)
                | ast::Expr::LoopExpr(_)
                | ast::Expr::ForExpr(_)
                | ast::Expr::WhileExpr(_)
                | ast::Expr::BlockExpr(_)
                | ast::Expr::MatchExpr(_)
        )
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ElseBranch {
    Block(ast::BlockExpr),
    IfExpr(ast::IfExpr),
}

impl From<ast::BlockExpr> for ElseBranch {
    fn from(block_expr: ast::BlockExpr) -> Self {
        Self::Block(block_expr)
    }
}

impl From<ast::IfExpr> for ElseBranch {
    fn from(if_expr: ast::IfExpr) -> Self {
        Self::IfExpr(if_expr)
    }
}

impl ast::IfExpr {
    pub fn then_branch(&self) -> Option<ast::BlockExpr> {
        self.children_after_condition().next()
    }

    pub fn else_branch(&self) -> Option<ElseBranch> {
        let res = match self.children_after_condition().nth(1) {
            Some(block) => ElseBranch::Block(block),
            None => {
                let elif = self.children_after_condition().next()?;
                ElseBranch::IfExpr(elif)
            }
        };
        Some(res)
    }

    fn children_after_condition<N: AstNode>(&self) -> impl Iterator<Item = N> {
        self.syntax().children().skip(1).filter_map(N::cast)
    }
}

#[test]
fn if_block_condition() {
    let parse = ast::SourceFile::parse(
        r#"
        fn test() {
            if { true } { "if" }
            else if { false } { "first elif" }
            else if true { "second elif" }
            else if (true) { "third elif" }
            else { "else" }
        }
        "#,
    );
    let if_ = parse.tree().syntax().descendants().find_map(ast::IfExpr::cast).unwrap();
    assert_eq!(if_.then_branch().unwrap().syntax().text(), r#"{ "if" }"#);
    let elif = match if_.else_branch().unwrap() {
        ElseBranch::IfExpr(elif) => elif,
        ElseBranch::Block(_) => panic!("should be `else if`"),
    };
    assert_eq!(elif.then_branch().unwrap().syntax().text(), r#"{ "first elif" }"#);
    let elif = match elif.else_branch().unwrap() {
        ElseBranch::IfExpr(elif) => elif,
        ElseBranch::Block(_) => panic!("should be `else if`"),
    };
    assert_eq!(elif.then_branch().unwrap().syntax().text(), r#"{ "second elif" }"#);
    let elif = match elif.else_branch().unwrap() {
        ElseBranch::IfExpr(elif) => elif,
        ElseBranch::Block(_) => panic!("should be `else if`"),
    };
    assert_eq!(elif.then_branch().unwrap().syntax().text(), r#"{ "third elif" }"#);
    let else_ = match elif.else_branch().unwrap() {
        ElseBranch::Block(else_) => else_,
        ElseBranch::IfExpr(_) => panic!("should be `else`"),
    };
    assert_eq!(else_.syntax().text(), r#"{ "else" }"#);
}

#[test]
fn if_condition_with_if_inside() {
    let parse = ast::SourceFile::parse(
        r#"
        fn test() {
            if if true { true } else { false } { "if" }
            else { "else" }
        }
        "#,
    );
    let if_ = parse.tree().syntax().descendants().find_map(ast::IfExpr::cast).unwrap();
    assert_eq!(if_.then_branch().unwrap().syntax().text(), r#"{ "if" }"#);
    let else_ = match if_.else_branch().unwrap() {
        ElseBranch::Block(else_) => else_,
        ElseBranch::IfExpr(_) => panic!("should be `else`"),
    };
    assert_eq!(else_.syntax().text(), r#"{ "else" }"#);
}

impl ast::PrefixExpr {
    pub fn op_kind(&self) -> Option<UnaryOp> {
        let res = match self.op_token()?.kind() {
            T![*] => UnaryOp::Deref,
            T![!] => UnaryOp::Not,
            T![-] => UnaryOp::Neg,
            _ => return None,
        };
        Some(res)
    }

    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.syntax().first_child_or_token()?.into_token()
    }
}

impl ast::BinExpr {
    pub fn op_details(&self) -> Option<(SyntaxToken, BinaryOp)> {
        self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {
            #[rustfmt::skip]
            let bin_op = match c.kind() {
                T![||] => BinaryOp::LogicOp(LogicOp::Or),
                T![&&] => BinaryOp::LogicOp(LogicOp::And),

                T![==] => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
                T![!=] => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
                T![<=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less,    strict: false }),
                T![>=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false }),
                T![<]  => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less,    strict: true }),
                T![>]  => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true }),

                T![+]  => BinaryOp::ArithOp(ArithOp::Add),
                T![*]  => BinaryOp::ArithOp(ArithOp::Mul),
                T![-]  => BinaryOp::ArithOp(ArithOp::Sub),
                T![/]  => BinaryOp::ArithOp(ArithOp::Div),
                T![%]  => BinaryOp::ArithOp(ArithOp::Rem),
                T![<<] => BinaryOp::ArithOp(ArithOp::Shl),
                T![>>] => BinaryOp::ArithOp(ArithOp::Shr),
                T![^]  => BinaryOp::ArithOp(ArithOp::BitXor),
                T![|]  => BinaryOp::ArithOp(ArithOp::BitOr),
                T![&]  => BinaryOp::ArithOp(ArithOp::BitAnd),

                T![=]   => BinaryOp::Assignment { op: None },
                T![+=]  => BinaryOp::Assignment { op: Some(ArithOp::Add) },
                T![*=]  => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
                T![-=]  => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
                T![/=]  => BinaryOp::Assignment { op: Some(ArithOp::Div) },
                T![%=]  => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
                T![<<=] => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
                T![>>=] => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
                T![^=]  => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
                T![|=]  => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
                T![&=]  => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },

                _ => return None,
            };
            Some((c, bin_op))
        })
    }

    pub fn op_kind(&self) -> Option<BinaryOp> {
        self.op_details().map(|t| t.1)
    }

    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.op_details().map(|t| t.0)
    }

    pub fn lhs(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).next()
    }

    pub fn rhs(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).nth(1)
    }

    pub fn sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>) {
        let mut children = support::children(self.syntax());
        let first = children.next();
        let second = children.next();
        (first, second)
    }
}

impl ast::RangeExpr {
    fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> {
        self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| {
            let token = child.into_token()?;
            let bin_op = match token.kind() {
                T![..] => RangeOp::Exclusive,
                T![..=] => RangeOp::Inclusive,
                _ => return None,
            };
            Some((ix, token, bin_op))
        })
    }

    pub fn op_kind(&self) -> Option<RangeOp> {
        self.op_details().map(|t| t.2)
    }

    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.op_details().map(|t| t.1)
    }

    pub fn start(&self) -> Option<ast::Expr> {
        let op_ix = self.op_details()?.0;
        self.syntax()
            .children_with_tokens()
            .take(op_ix)
            .find_map(|it| ast::Expr::cast(it.into_node()?))
    }

    pub fn end(&self) -> Option<ast::Expr> {
        let op_ix = self.op_details()?.0;
        self.syntax()
            .children_with_tokens()
            .skip(op_ix + 1)
            .find_map(|it| ast::Expr::cast(it.into_node()?))
    }
}

impl ast::IndexExpr {
    pub fn base(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).next()
    }
    pub fn index(&self) -> Option<ast::Expr> {
        support::children(self.syntax()).nth(1)
    }
}

pub enum ArrayExprKind {
    Repeat { initializer: Option<ast::Expr>, repeat: Option<ast::Expr> },
    ElementList(AstChildren<ast::Expr>),
}

impl ast::ArrayExpr {
    pub fn kind(&self) -> ArrayExprKind {
        if self.is_repeat() {
            ArrayExprKind::Repeat {
                initializer: support::children(self.syntax()).next(),
                repeat: support::children(self.syntax()).nth(1),
            }
        } else {
            ArrayExprKind::ElementList(support::children(self.syntax()))
        }
    }

    fn is_repeat(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![;])
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum LiteralKind {
    String(ast::String),
    ByteString(ast::ByteString),
    IntNumber(ast::IntNumber),
    FloatNumber(ast::FloatNumber),
    Char,
    Byte,
    Bool(bool),
}

impl ast::Literal {
    pub fn token(&self) -> SyntaxToken {
        self.syntax()
            .children_with_tokens()
            .find(|e| e.kind() != ATTR && !e.kind().is_trivia())
            .and_then(|e| e.into_token())
            .unwrap()
    }

    pub fn kind(&self) -> LiteralKind {
        let token = self.token();

        if let Some(t) = ast::IntNumber::cast(token.clone()) {
            return LiteralKind::IntNumber(t);
        }
        if let Some(t) = ast::FloatNumber::cast(token.clone()) {
            return LiteralKind::FloatNumber(t);
        }
        if let Some(t) = ast::String::cast(token.clone()) {
            return LiteralKind::String(t);
        }
        if let Some(t) = ast::ByteString::cast(token.clone()) {
            return LiteralKind::ByteString(t);
        }

        match token.kind() {
            T![true] => LiteralKind::Bool(true),
            T![false] => LiteralKind::Bool(false),
            CHAR => LiteralKind::Char,
            BYTE => LiteralKind::Byte,
            _ => unreachable!(),
        }
    }
}

pub enum BlockModifier {
    Async(SyntaxToken),
    Unsafe(SyntaxToken),
    Try(SyntaxToken),
    Const(SyntaxToken),
    Label(ast::Label),
}

impl ast::BlockExpr {
    pub fn modifier(&self) -> Option<BlockModifier> {
        self.async_token()
            .map(BlockModifier::Async)
            .or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
            .or_else(|| self.try_token().map(BlockModifier::Try))
            .or_else(|| self.const_token().map(BlockModifier::Const))
            .or_else(|| self.label().map(BlockModifier::Label))
    }
    /// false if the block is an intrinsic part of the syntax and can't be
    /// replaced with arbitrary expression.
    ///
    /// ```not_rust
    /// fn foo() { not_stand_alone }
    /// const FOO: () = { stand_alone };
    /// ```
    pub fn is_standalone(&self) -> bool {
        let parent = match self.syntax().parent() {
            Some(it) => it,
            None => return true,
        };
        !matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR)
    }
}

#[test]
fn test_literal_with_attr() {
    let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
    let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap();
    assert_eq!(lit.token().text(), r#""Hello""#);
}

impl ast::RecordExprField {
    pub fn parent_record_lit(&self) -> ast::RecordExpr {
        self.syntax().ancestors().find_map(ast::RecordExpr::cast).unwrap()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CallableExpr {
    Call(ast::CallExpr),
    MethodCall(ast::MethodCallExpr),
}

impl ast::HasAttrs for CallableExpr {}
impl ast::HasArgList for CallableExpr {}

impl AstNode for CallableExpr {
    fn can_cast(kind: parser::SyntaxKind) -> bool
    where
        Self: Sized,
    {
        ast::CallExpr::can_cast(kind) || ast::MethodCallExpr::can_cast(kind)
    }

    fn cast(syntax: SyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if let Some(it) = ast::CallExpr::cast(syntax.clone()) {
            Some(Self::Call(it))
        } else if let Some(it) = ast::MethodCallExpr::cast(syntax) {
            Some(Self::MethodCall(it))
        } else {
            None
        }
    }

    fn syntax(&self) -> &SyntaxNode {
        match self {
            Self::Call(it) => it.syntax(),
            Self::MethodCall(it) => it.syntax(),
        }
    }
}
<|endoftext|>


<commit_before>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd,
    WorkDoneProgressReport,
};
use parking_lot::RwLock;
use std::{
    collections::HashMap,
    path::PathBuf,
    process::{Command, Stdio},
    sync::Arc,
    thread::JoinHandle,
    time::Instant,
};

mod conv;

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix};

#[derive(Clone, Debug)]
pub struct CheckOptions {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    pub task_recv: Receiver<CheckTask>,
    pub cmd_send: Option<Sender<CheckCommand>>,
    pub shared: Arc<RwLock<CheckWatcherSharedState>>,
    handle: Option<JoinHandle<()>>,
}

impl CheckWatcher {
    pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
        let options = options.clone();
        let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new()));

        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let shared_ = shared.clone();
        let handle = std::thread::spawn(move || {
            let mut check = CheckWatcherState::new(options, workspace_root, shared_);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), shared }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        if let Some(cmd_send) = &self.cmd_send {
            cmd_send.send(CheckCommand::Update).unwrap();
        }
    }
}

impl std::ops::Drop for CheckWatcher {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Take the sender out of the option
            let recv = self.cmd_send.take();

            // Dropping the sender finishes the thread loop
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}

#[derive(Debug)]
pub struct CheckWatcherSharedState {
    diagnostic_collection: HashMap<Url, Vec<Diagnostic>>,
    suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>,
}

impl CheckWatcherSharedState {
    fn new() -> CheckWatcherSharedState {
        CheckWatcherSharedState {
            diagnostic_collection: HashMap::new(),
            suggested_fix_collection: HashMap::new(),
        }
    }

    /// Clear the cached diagnostics, and schedule updating diagnostics by the
    /// server, to clear stale results.
    pub fn clear(&mut self, task_send: &Sender<CheckTask>) {
        let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect();

        self.diagnostic_collection.clear();
        self.suggested_fix_collection.clear();

        for uri in cleared_files {
            task_send.send(CheckTask::Update(uri.clone())).unwrap();
        }
    }

    pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> {
        self.diagnostic_collection.get(uri).map(|d| d.as_slice())
    }

    pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> {
        self.suggested_fix_collection.get(uri).map(|d| d.as_slice())
    }

    fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) {
        let diagnostics = self.diagnostic_collection.entry(file_uri).or_default();

        // If we're building multiple targets it's possible we've already seen this diagnostic
        let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic));
        if is_duplicate {
            return;
        }

        diagnostics.push(diagnostic);
    }

    fn add_suggested_fix_for_diagnostic(
        &mut self,
        mut suggested_fix: SuggestedFix,
        diagnostic: &Diagnostic,
    ) {
        let file_uri = suggested_fix.location.uri.clone();
        let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default();

        let existing_suggestion: Option<&mut SuggestedFix> =
            file_suggestions.iter_mut().find(|s| s == &&suggested_fix);
        if let Some(existing_suggestion) = existing_suggestion {
            // The existing suggestion also applies to this new diagnostic
            existing_suggestion.diagnostics.push(diagnostic.clone());
        } else {
            // We haven't seen this suggestion before
            suggested_fix.diagnostics.push(diagnostic.clone());
            file_suggestions.push(suggested_fix);
        }
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a update of the given files diagnostics
    Update(Url),

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherState {
    options: CheckOptions,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
    shared: Arc<RwLock<CheckWatcherSharedState>>,
}

impl CheckWatcherState {
    pub fn new(
        options: CheckOptions,
        workspace_root: PathBuf,
        shared: Arc<RwLock<CheckWatcherSharedState>>,
    ) -> CheckWatcherState {
        let watcher = WatchThread::new(&options, &workspace_root);
        CheckWatcherState { options, workspace_root, watcher, last_update_req: None, shared }
    }

    pub fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                self.shared.write().clear(task_send);

                // By replacing the watcher, we drop the previous one which
                // causes it to shut down automatically.
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result =
                    match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) {
                        Some(map_result) => map_result,
                        None => return,
                    };

                let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result;
                let file_uri = location.uri.clone();

                if !suggested_fixes.is_empty() {
                    for suggested_fix in suggested_fixes {
                        self.shared
                            .write()
                            .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic);
                    }
                }
                self.shared.write().add_diagnostic(file_uri, diagnostic);

                task_send.send(CheckTask::Update(location.uri)).unwrap();
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    handle: Option<JoinHandle<()>>,
    message_recv: Receiver<CheckEvent>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

impl WatchThread {
    fn new(options: &CheckOptions, workspace_root: &PathBuf) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.to_string_lossy()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let enabled = options.enable;
        let handle = std::thread::spawn(move || {
            if !enabled {
                return;
            }

            let mut command = Command::new("cargo")
                .args(&args)
                .stdout(Stdio::piped())
                .stderr(Stdio::null())
                .spawn()
                .expect("couldn't launch cargo");

            // If we trigger an error here, we will do so in the loop instead,
            // which will break out of the loop, and continue the shutdown
            let _ = message_send.send(CheckEvent::Begin);

            for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) {
                let message = match message {
                    Ok(message) => message,
                    Err(err) => {
                        log::error!("Invalid json from cargo check, ignoring: {}", err);
                        continue;
                    }
                };

                match message_send.send(CheckEvent::Msg(message)) {
                    Ok(()) => {}
                    Err(_err) => {
                        // The send channel was closed, so we want to shutdown
                        break;
                    }
                }
            }

            // We can ignore any error here, as we are already in the progress
            // of shutting down.
            let _ = message_send.send(CheckEvent::End);

            // It is okay to ignore the result, as it only errors if the process is already dead
            let _ = command.kill();

            // Again, we don't care about the exit status so just ignore the result
            let _ = command.wait();
        });
        WatchThread { handle: Some(handle), message_recv }
    }
}

impl std::ops::Drop for WatchThread {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Replace our reciever with dummy one, so we can drop and close the
            // one actually communicating with the thread
            let recv = std::mem::replace(&mut self.message_recv, never());

            // Dropping the original reciever initiates thread sub-process shutdown
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}

fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool {
    left.source == right.source
        && left.severity == right.severity
        && left.range == right.range
        && left.message == right.message
}
<commit_msg>Reduce visibility<commit_after>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd,
    WorkDoneProgressReport,
};
use parking_lot::RwLock;
use std::{
    collections::HashMap,
    path::PathBuf,
    process::{Command, Stdio},
    sync::Arc,
    thread::JoinHandle,
    time::Instant,
};

mod conv;

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix};

#[derive(Clone, Debug)]
pub struct CheckOptions {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    pub task_recv: Receiver<CheckTask>,
    pub shared: Arc<RwLock<CheckWatcherSharedState>>,
    cmd_send: Option<Sender<CheckCommand>>,
    handle: Option<JoinHandle<()>>,
}

impl CheckWatcher {
    pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
        let options = options.clone();
        let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new()));

        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let shared_ = shared.clone();
        let handle = std::thread::spawn(move || {
            let mut check = CheckWatcherState::new(options, workspace_root, shared_);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), shared }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        if let Some(cmd_send) = &self.cmd_send {
            cmd_send.send(CheckCommand::Update).unwrap();
        }
    }
}

impl std::ops::Drop for CheckWatcher {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Take the sender out of the option
            let recv = self.cmd_send.take();

            // Dropping the sender finishes the thread loop
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}

#[derive(Debug)]
pub struct CheckWatcherSharedState {
    diagnostic_collection: HashMap<Url, Vec<Diagnostic>>,
    suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>,
}

impl CheckWatcherSharedState {
    fn new() -> CheckWatcherSharedState {
        CheckWatcherSharedState {
            diagnostic_collection: HashMap::new(),
            suggested_fix_collection: HashMap::new(),
        }
    }

    /// Clear the cached diagnostics, and schedule updating diagnostics by the
    /// server, to clear stale results.
    pub fn clear(&mut self, task_send: &Sender<CheckTask>) {
        let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect();

        self.diagnostic_collection.clear();
        self.suggested_fix_collection.clear();

        for uri in cleared_files {
            task_send.send(CheckTask::Update(uri.clone())).unwrap();
        }
    }

    pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> {
        self.diagnostic_collection.get(uri).map(|d| d.as_slice())
    }

    pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> {
        self.suggested_fix_collection.get(uri).map(|d| d.as_slice())
    }

    fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) {
        let diagnostics = self.diagnostic_collection.entry(file_uri).or_default();

        // If we're building multiple targets it's possible we've already seen this diagnostic
        let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic));
        if is_duplicate {
            return;
        }

        diagnostics.push(diagnostic);
    }

    fn add_suggested_fix_for_diagnostic(
        &mut self,
        mut suggested_fix: SuggestedFix,
        diagnostic: &Diagnostic,
    ) {
        let file_uri = suggested_fix.location.uri.clone();
        let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default();

        let existing_suggestion: Option<&mut SuggestedFix> =
            file_suggestions.iter_mut().find(|s| s == &&suggested_fix);
        if let Some(existing_suggestion) = existing_suggestion {
            // The existing suggestion also applies to this new diagnostic
            existing_suggestion.diagnostics.push(diagnostic.clone());
        } else {
            // We haven't seen this suggestion before
            suggested_fix.diagnostics.push(diagnostic.clone());
            file_suggestions.push(suggested_fix);
        }
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a update of the given files diagnostics
    Update(Url),

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherState {
    options: CheckOptions,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
    shared: Arc<RwLock<CheckWatcherSharedState>>,
}

impl CheckWatcherState {
    fn new(
        options: CheckOptions,
        workspace_root: PathBuf,
        shared: Arc<RwLock<CheckWatcherSharedState>>,
    ) -> CheckWatcherState {
        let watcher = WatchThread::new(&options, &workspace_root);
        CheckWatcherState { options, workspace_root, watcher, last_update_req: None, shared }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                self.shared.write().clear(task_send);

                // By replacing the watcher, we drop the previous one which
                // causes it to shut down automatically.
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result =
                    match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) {
                        Some(map_result) => map_result,
                        None => return,
                    };

                let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result;
                let file_uri = location.uri.clone();

                if !suggested_fixes.is_empty() {
                    for suggested_fix in suggested_fixes {
                        self.shared
                            .write()
                            .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic);
                    }
                }
                self.shared.write().add_diagnostic(file_uri, diagnostic);

                task_send.send(CheckTask::Update(location.uri)).unwrap();
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    handle: Option<JoinHandle<()>>,
    message_recv: Receiver<CheckEvent>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

impl WatchThread {
    fn new(options: &CheckOptions, workspace_root: &PathBuf) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.to_string_lossy()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let enabled = options.enable;
        let handle = std::thread::spawn(move || {
            if !enabled {
                return;
            }

            let mut command = Command::new("cargo")
                .args(&args)
                .stdout(Stdio::piped())
                .stderr(Stdio::null())
                .spawn()
                .expect("couldn't launch cargo");

            // If we trigger an error here, we will do so in the loop instead,
            // which will break out of the loop, and continue the shutdown
            let _ = message_send.send(CheckEvent::Begin);

            for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) {
                let message = match message {
                    Ok(message) => message,
                    Err(err) => {
                        log::error!("Invalid json from cargo check, ignoring: {}", err);
                        continue;
                    }
                };

                match message_send.send(CheckEvent::Msg(message)) {
                    Ok(()) => {}
                    Err(_err) => {
                        // The send channel was closed, so we want to shutdown
                        break;
                    }
                }
            }

            // We can ignore any error here, as we are already in the progress
            // of shutting down.
            let _ = message_send.send(CheckEvent::End);

            // It is okay to ignore the result, as it only errors if the process is already dead
            let _ = command.kill();

            // Again, we don't care about the exit status so just ignore the result
            let _ = command.wait();
        });
        WatchThread { handle: Some(handle), message_recv }
    }
}

impl std::ops::Drop for WatchThread {
    fn drop(&mut self) {
        if let Some(handle) = self.handle.take() {
            // Replace our reciever with dummy one, so we can drop and close the
            // one actually communicating with the thread
            let recv = std::mem::replace(&mut self.message_recv, never());

            // Dropping the original reciever initiates thread sub-process shutdown
            drop(recv);

            // Join the thread, it should finish shortly. We don't really care
            // whether it panicked, so it is safe to ignore the result
            let _ = handle.join();
        }
    }
}

fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool {
    left.source == right.source
        && left.severity == right.severity
        && left.range == right.range
        && left.message == right.message
}
<|endoftext|>


<commit_before>//! The forwarder module shall act as wrapper for the ffi mangled lib.rs
//! calls, which shall be very thin and spare there, but extensive here
//! to prepare clean calls to adbfflutter.
use crate::{
    common::paths::SearchPath,
    ctrl::{ForwardNetMsg, UiUpdateMsg},
    data::{collection::Collection, ipc::IPC},
    net::subs::peer_representation,
    shared,
};
use async_std::task;
use crossbeam::{sync::WaitGroup, unbounded};
use std::{
    sync::{mpsc::channel, Arc, Mutex},
    thread,
};

/// just return the number of audio files found for now
pub fn ffi_file_count_good(input_path: Vec<String>) -> u32 {
    // prepare data
    let cleaned_paths = SearchPath::new(&input_path);
    let search_path = Arc::new(Mutex::new(cleaned_paths));

    let (tx, _rx) = channel::<UiUpdateMsg>();
    let synced_to_ui_messages = Arc::new(Mutex::new(tx.clone()));
    let has_ui = false;

    // set up data
    let collection_protected = Arc::new(Mutex::new(Collection::new()));
    let output_data_return_handle = shared::collection_search(
        collection_protected,
        search_path,
        synced_to_ui_messages,
        has_ui,
    );

    // scope and block trickery for lifetime and mutability
    output_data_return_handle.nr_found_songs
}

/// return the peer hash for testing yet
pub async fn ffi_new_peer() -> u64 {
    //
    let dummy_wait_net_thread = WaitGroup::new();
    let (ui_sender, reactor) = channel::<UiUpdateMsg>();
    let (_, dummy_ipc_receive) = unbounded::<IPC>();

    // initiate a thread (which will be ... (todo: lazy_static maybe later, and keep it running)
    // which handles net communication
    let single_shot_net_thread = thread::Builder::new()
        .name("app_net".into())
        .spawn(move || {
            task::block_on(async move {
                let _ =
                    shared::net_search(dummy_wait_net_thread, Some(ui_sender), dummy_ipc_receive)
                        .await;
            });
        })
        .unwrap();

    // very interesting, the compiler is awesome!!
    let out;

    // loop over fixme: (very cheap version here, that could be done more elegantly)
    loop {
        if let Ok(reaction) = reactor.try_recv() {
            match reaction {
                UiUpdateMsg::CollectionUpdate(_, _) => {}
                UiUpdateMsg::NetUpdate(net_message) => {
                    match net_message {
                        ForwardNetMsg::Add(peer) => {
                            // yet only id is interesting
                            out = peer_representation::peer_to_hash(&peer.id);
                            break;
                        }
                        ForwardNetMsg::Delete(_peer_id) => {}
                        ForwardNetMsg::Stats(_) => {}
                    }
                }
                UiUpdateMsg::PeerSearchFinished(_peer_id, _data) => {}
                UiUpdateMsg::StopUI => {}
            }
        }
    }
    // kill this big thread / fixme: very inefficent yet
    drop(single_shot_net_thread);
    out
}
<commit_msg>[documentation] a bit more plus check if github for tagged works<commit_after>//! The forwarder module shall act as wrapper for the ffi mangled lib.rs
//! calls, which shall be very thin and spare there, but extensive here
//! to prepare clean calls to adbfflutter.
use crate::{
    common::paths::SearchPath,
    ctrl::{ForwardNetMsg, UiUpdateMsg},
    data::{collection::Collection, ipc::IPC},
    net::subs::peer_representation,
    shared,
};
use async_std::task;
use crossbeam::{sync::WaitGroup, unbounded};
use std::{
    sync::{mpsc::channel, Arc, Mutex},
    thread,
};

/// just return the number of audio files found for now
pub fn ffi_file_count_good(input_path: Vec<String>) -> u32 {
    // prepare data
    let cleaned_paths = SearchPath::new(&input_path);
    let search_path = Arc::new(Mutex::new(cleaned_paths));

    let (tx, _rx) = channel::<UiUpdateMsg>();
    let synced_to_ui_messages = Arc::new(Mutex::new(tx.clone()));
    let has_ui = false;

    // set up data
    let collection_protected = Arc::new(Mutex::new(Collection::new()));
    let output_data_return_handle = shared::collection_search(
        collection_protected,
        search_path,
        synced_to_ui_messages,
        has_ui,
    );

    // scope and block trickery for lifetime and mutability
    output_data_return_handle.nr_found_songs
}

/// return the peer hash for testing yet
pub async fn ffi_new_peer() -> u64 {
    //
    let dummy_wait_net_thread = WaitGroup::new();
    let (ui_sender, reactor) = channel::<UiUpdateMsg>();
    let (_, dummy_ipc_receive) = unbounded::<IPC>();

    // todo: make it a lazy static as RUNTIME, to also hold the same IP, otherwise it will
    //       always change it's ip!
    // initiate a thread (which will be ... (todo: lazy_static maybe later, and keep it running)
    // which handles net communication
    let single_shot_net_thread = thread::Builder::new()
        .name("app_net".into())
        .spawn(move || {
            task::block_on(async move {
                let _ =
                    shared::net_search(dummy_wait_net_thread, Some(ui_sender), dummy_ipc_receive)
                        .await;
            });
        })
        .unwrap();

    // very interesting, the compiler is awesome!!
    let out;

    // loop over fixme: (very cheap version here, that could be done more elegantly)
    loop {
        if let Ok(reaction) = reactor.try_recv() {
            match reaction {
                UiUpdateMsg::CollectionUpdate(_, _) => {}
                UiUpdateMsg::NetUpdate(net_message) => {
                    match net_message {
                        ForwardNetMsg::Add(peer) => {
                            // yet only id is interesting
                            out = peer_representation::peer_to_hash(&peer.id);
                            break;
                        }
                        ForwardNetMsg::Delete(_peer_id) => {}
                        ForwardNetMsg::Stats(_) => {}
                    }
                }
                UiUpdateMsg::PeerSearchFinished(_peer_id, _data) => {}
                UiUpdateMsg::StopUI => {}
            }
        }
    }
    // kill this big thread / fixme: very inefficent yet
    drop(single_shot_net_thread);
    out
}
<|endoftext|>


<commit_before>use graph::test::TestGraph;
use super::reachable;

#[test]
fn test1() {
    // 0 -> 1 -> 2 -> 3
    //      ^    v
    //      6 <- 4 -> 5
    let graph = TestGraph::new(0, &[
        (0, 1),
        (1, 2),
        (2, 3),
        (2, 4),
        (4, 5),
        (4, 6),
        (6, 1),
    ]);
    let reachable = reachable(&graph);
    assert!((0..6).all(|i| reachable.can_reach(0, i)));
    assert!((1..6).all(|i| reachable.can_reach(1, i)));
    assert!((1..6).all(|i| reachable.can_reach(2, i)));
    assert!((1..6).all(|i| reachable.can_reach(4, i)));
    assert!((1..6).all(|i| reachable.can_reach(6, i)));
    assert!(reachable.can_reach(3, 3));
    assert!(!reachable.can_reach(3, 5));
    assert!(!reachable.can_reach(5, 3));
}
<commit_msg>add a simple test that cross over words<commit_after>use graph::test::TestGraph;
use super::reachable;

#[test]
fn test1() {
    // 0 -> 1 -> 2 -> 3
    //      ^    v
    //      6 <- 4 -> 5
    let graph = TestGraph::new(0, &[
        (0, 1),
        (1, 2),
        (2, 3),
        (2, 4),
        (4, 5),
        (4, 6),
        (6, 1),
    ]);
    let reachable = reachable(&graph);
    assert!((0..6).all(|i| reachable.can_reach(0, i)));
    assert!((1..6).all(|i| reachable.can_reach(1, i)));
    assert!((1..6).all(|i| reachable.can_reach(2, i)));
    assert!((1..6).all(|i| reachable.can_reach(4, i)));
    assert!((1..6).all(|i| reachable.can_reach(6, i)));
    assert!(reachable.can_reach(3, 3));
    assert!(!reachable.can_reach(3, 5));
    assert!(!reachable.can_reach(5, 3));
}

/// use bigger indices to cross between words in the bit set
#[test]
fn test2() {
    // 30 -> 31 -> 32 -> 33
    //       ^      v
    //       36 <- 34 -> 35
    let graph = TestGraph::new(30, &[
        (30, 31),
        (31, 32),
        (32, 33),
        (32, 34),
        (34, 35),
        (34, 36),
        (36, 31),
    ]);
    let reachable = reachable(&graph);
    assert!((30..36).all(|i| reachable.can_reach(30, i)));
    assert!((31..36).all(|i| reachable.can_reach(31, i)));
    assert!((31..36).all(|i| reachable.can_reach(32, i)));
    assert!((31..36).all(|i| reachable.can_reach(34, i)));
    assert!((31..36).all(|i| reachable.can_reach(36, i)));
    assert!(reachable.can_reach(33, 33));
    assert!(!reachable.can_reach(33, 35));
    assert!(!reachable.can_reach(35, 33));
}
<|endoftext|>