<commit_before>use std::error::Error;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ParseError<L, T, E> {
    /// Generated by the parser when it encounters a token (or EOF) it did not
    /// expect.
    InvalidToken { location: L },

    /// Generated by the parser when it encounters a token (or EOF) it did not
    /// expect.
    UnrecognizedToken {
        /// If this is `Some`, then an unexpected token of type `T`
        /// was observed, with a span given by the two `L` values. If
        /// this is `None`, then EOF was observed when it was not
        /// expected.
        token: Option<(L, T, L)>,

        /// The set of expected tokens: these names are taken from the
        /// grammar and hence may not necessarily be suitable for
        /// presenting to the user.
        expected: Vec<String>,
    },

    /// Generated by the parser when it encounters additional,
    /// unexpected tokens.
    ExtraToken { token: (L, T, L) },

    /// Custom error type.
    User { error: E },
}

impl<L, T, E> ParseError<L, T, E> {
    fn map_intern<FL, LL, FT, TT, FE, EE>(
        self,
        loc_op: FL,
        tok_op: FT,
        err_op: FE,
    ) -> ParseError<LL, TT, EE>
    where
        FL: Fn(L) -> LL,
        FT: Fn(T) -> TT,
        FE: Fn(E) -> EE,
    {
        let maptok = |(s, t, e): (L, T, L)| (loc_op(s), tok_op(t), loc_op(e));
        match self {
            ParseError::InvalidToken { location } => ParseError::InvalidToken {
                location: loc_op(location),
            },
            ParseError::UnrecognizedToken { token, expected } => ParseError::UnrecognizedToken {
                token: token.map(maptok),
                expected: expected,
            },
            ParseError::ExtraToken { token } => ParseError::ExtraToken {
                token: maptok(token),
            },
            ParseError::User { error } => ParseError::User {
                error: err_op(error),
            },
        }
    }

    pub fn map_location<F, LL>(self, op: F) -> ParseError<LL, T, E>
    where
        F: Fn(L) -> LL,
    {
        self.map_intern(op, |x| x, |x| x)
    }

    pub fn map_token<F, TT>(self, op: F) -> ParseError<L, TT, E>
    where
        F: Fn(T) -> TT,
    {
        self.map_intern(|x| x, op, |x| x)
    }

    pub fn map_error<F, EE>(self, op: F) -> ParseError<L, T, EE>
    where
        F: Fn(E) -> EE,
    {
        self.map_intern(|x| x, |x| x, op)
    }
}

impl<L, T, E> fmt::Display for ParseError<L, T, E>
where
    L: fmt::Display,
    T: fmt::Display,
    E: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ParseError::*;
        match *self {
            InvalidToken { ref location } => write!(f, "Invalid token at {}", location),
            UnrecognizedToken {
                ref token,
                ref expected,
            } => {
                match *token {
                    Some((ref start, ref token, ref end)) => try!(write!(
                        f,
                        "Unrecognized token `{}` found at {}:{}",
                        token, start, end
                    )),
                    None => try!(write!(f, "Unrecognized EOF")),
                }
                if !expected.is_empty() {
                    try!(writeln!(f, ""));
                    for (i, e) in expected.iter().enumerate() {
                        let sep = match i {
                            0 => "Expected one of",
                            _ if i < expected.len() - 1 => ",",
                            // Last expected message to be written
                            _ => " or",
                        };
                        try!(write!(f, "{} {}", sep, e));
                    }
                }
                Ok(())
            }
            ExtraToken {
                token: (ref start, ref token, ref end),
            } => write!(f, "Extra token {} found at {}:{}", token, start, end),
            User { ref error } => write!(f, "{}", error),
        }
    }
}

impl<L, T, E> Error for ParseError<L, T, E>
where
    L: fmt::Debug + fmt::Display,
    T: fmt::Debug + fmt::Display,
    E: fmt::Debug + fmt::Display,
{
    fn description(&self) -> &str {
        "parse error"
    }
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ErrorRecovery<L, T, E> {
    pub error: ParseError<L, T, E>,
    pub dropped_tokens: Vec<(L, T, L)>,
}

/// Define a module using the generated parse from a `.lalrpop` file.
///
/// You have to specify the name of the module and the path of the file
/// generated by LALRPOP. If the input is in the root directory, you can
/// omit it.
///
/// # Example
/// ```ignore
/// // load parser in src/parser.lalrpop
/// lalrpop_mod!(parser);
///
/// // load parser in src/lex/parser.lalrpop
/// lalrpop_mod!(parser, "/lex/parser.rs");
///
/// // define a public module
/// lalrpop_mod!(pub parser);
/// ```

#[macro_export]
macro_rules! lalrpop_mod {
    ($modname:ident) => { lalrpop_mod!($modname, concat!("/", stringify!($modname), ".rs")); };

    (pub $modname:ident) => { lalrpop_mod!(pub $modname, concat!("/", stringify!($modname), ".rs")); };

    ($modname:ident, $source:expr) => { mod $modname { include!(concat!(env!("OUT_DIR"), $source)); } };

    (pub $modname:ident, $source:expr) => { pub mod $modname { include!(concat!(env!("OUT_DIR"), $source)); } };
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test() {
        let err = ParseError::UnrecognizedToken::<i32, &str, &str> {
            token: Some((1, "t0", 2)),
            expected: vec!["t1", "t2", "t3"]
                .into_iter()
                .map(|s| s.to_string())
                .collect(),
        };
        assert_eq!(
            format!("{}", err),
            "Unrecognized token `t0` found at 1:2\n\
             Expected one of t1, t2 or t3"
        );
    }
}
<commit_msg>feat: Allow attributes to be specified in lalrpop_mod!<commit_after>use std::error::Error;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ParseError<L, T, E> {
    /// Generated by the parser when it encounters a token (or EOF) it did not
    /// expect.
    InvalidToken { location: L },

    /// Generated by the parser when it encounters a token (or EOF) it did not
    /// expect.
    UnrecognizedToken {
        /// If this is `Some`, then an unexpected token of type `T`
        /// was observed, with a span given by the two `L` values. If
        /// this is `None`, then EOF was observed when it was not
        /// expected.
        token: Option<(L, T, L)>,

        /// The set of expected tokens: these names are taken from the
        /// grammar and hence may not necessarily be suitable for
        /// presenting to the user.
        expected: Vec<String>,
    },

    /// Generated by the parser when it encounters additional,
    /// unexpected tokens.
    ExtraToken { token: (L, T, L) },

    /// Custom error type.
    User { error: E },
}

impl<L, T, E> ParseError<L, T, E> {
    fn map_intern<FL, LL, FT, TT, FE, EE>(
        self,
        loc_op: FL,
        tok_op: FT,
        err_op: FE,
    ) -> ParseError<LL, TT, EE>
    where
        FL: Fn(L) -> LL,
        FT: Fn(T) -> TT,
        FE: Fn(E) -> EE,
    {
        let maptok = |(s, t, e): (L, T, L)| (loc_op(s), tok_op(t), loc_op(e));
        match self {
            ParseError::InvalidToken { location } => ParseError::InvalidToken {
                location: loc_op(location),
            },
            ParseError::UnrecognizedToken { token, expected } => ParseError::UnrecognizedToken {
                token: token.map(maptok),
                expected: expected,
            },
            ParseError::ExtraToken { token } => ParseError::ExtraToken {
                token: maptok(token),
            },
            ParseError::User { error } => ParseError::User {
                error: err_op(error),
            },
        }
    }

    pub fn map_location<F, LL>(self, op: F) -> ParseError<LL, T, E>
    where
        F: Fn(L) -> LL,
    {
        self.map_intern(op, |x| x, |x| x)
    }

    pub fn map_token<F, TT>(self, op: F) -> ParseError<L, TT, E>
    where
        F: Fn(T) -> TT,
    {
        self.map_intern(|x| x, op, |x| x)
    }

    pub fn map_error<F, EE>(self, op: F) -> ParseError<L, T, EE>
    where
        F: Fn(E) -> EE,
    {
        self.map_intern(|x| x, |x| x, op)
    }
}

impl<L, T, E> fmt::Display for ParseError<L, T, E>
where
    L: fmt::Display,
    T: fmt::Display,
    E: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ParseError::*;
        match *self {
            InvalidToken { ref location } => write!(f, "Invalid token at {}", location),
            UnrecognizedToken {
                ref token,
                ref expected,
            } => {
                match *token {
                    Some((ref start, ref token, ref end)) => try!(write!(
                        f,
                        "Unrecognized token `{}` found at {}:{}",
                        token, start, end
                    )),
                    None => try!(write!(f, "Unrecognized EOF")),
                }
                if !expected.is_empty() {
                    try!(writeln!(f, ""));
                    for (i, e) in expected.iter().enumerate() {
                        let sep = match i {
                            0 => "Expected one of",
                            _ if i < expected.len() - 1 => ",",
                            // Last expected message to be written
                            _ => " or",
                        };
                        try!(write!(f, "{} {}", sep, e));
                    }
                }
                Ok(())
            }
            ExtraToken {
                token: (ref start, ref token, ref end),
            } => write!(f, "Extra token {} found at {}:{}", token, start, end),
            User { ref error } => write!(f, "{}", error),
        }
    }
}

impl<L, T, E> Error for ParseError<L, T, E>
where
    L: fmt::Debug + fmt::Display,
    T: fmt::Debug + fmt::Display,
    E: fmt::Debug + fmt::Display,
{
    fn description(&self) -> &str {
        "parse error"
    }
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ErrorRecovery<L, T, E> {
    pub error: ParseError<L, T, E>,
    pub dropped_tokens: Vec<(L, T, L)>,
}

/// Define a module using the generated parse from a `.lalrpop` file.
///
/// You have to specify the name of the module and the path of the file
/// generated by LALRPOP. If the input is in the root directory, you can
/// omit it.
///
/// # Example
/// ```ignore
/// // load parser in src/parser.lalrpop
/// lalrpop_mod!(parser);
///
/// // load parser in src/lex/parser.lalrpop
/// lalrpop_mod!(parser, "/lex/parser.rs");
///
/// // define a public module
/// lalrpop_mod!(pub parser);
/// ```

#[macro_export]
macro_rules! lalrpop_mod {
    ($(#[$attr:meta])* $modname:ident) => {
        lalrpop_mod!($(#[$attr])* $modname, concat!("/", stringify!($modname), ".rs"));
    };

    ($(#[$attr:meta])* pub $modname:ident) => {
        lalrpop_mod!($(#[$attr])* pub $modname, concat!("/", stringify!($modname), ".rs"));
    };

    ($(#[$attr:meta])* $modname:ident, $source:expr) => {
        $(#[$attr])* mod $modname { include!(concat!(env!("OUT_DIR"), $source)); }
    };

    ($(#[$attr:meta])* pub $modname:ident, $source:expr) => {
        $(#[$attr])* pub mod $modname { include!(concat!(env!("OUT_DIR"), $source)); }
    };
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test() {
        let err = ParseError::UnrecognizedToken::<i32, &str, &str> {
            token: Some((1, "t0", 2)),
            expected: vec!["t1", "t2", "t3"]
                .into_iter()
                .map(|s| s.to_string())
                .collect(),
        };
        assert_eq!(
            format!("{}", err),
            "Unrecognized token `t0` found at 1:2\n\
             Expected one of t1, t2 or t3"
        );
    }
}
<|endoftext|>


<commit_before>use crate::common::util::*;
#[cfg(not(windows))]
use std::os::unix::fs::PermissionsExt;
extern crate libc;
use self::libc::{umask, mode_t};

static TEST_DIR1: &str = "mkdir_test1";
static TEST_DIR2: &str = "mkdir_test2";
static TEST_DIR3: &str = "mkdir_test3";
static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &str = "mkdir_test6";
static TEST_FILE7: &str = "mkdir_test7";
static TEST_DIR8: &str = "mkdir_test8";

#[test]
fn test_mkdir_mkdir() {
        new_ucmd!().arg(TEST_DIR1).succeeds();
}

#[test]
fn test_mkdir_verbose() {
    let expected = "mkdir: created directory 'mkdir_test1'\n";
    new_ucmd!()
        .arg(TEST_DIR1)
        .arg("-v")
        .run()
        .stdout_is(expected);
}

#[test]
fn test_mkdir_dup_dir() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR2).succeeds();
    scene.ucmd().arg(TEST_DIR2).fails();
}

#[test]
fn test_mkdir_mode() {
    new_ucmd!().arg("-m").arg("755").arg(TEST_DIR3).succeeds();
}

#[test]
fn test_mkdir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parent").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parents").arg(TEST_DIR4).succeeds();
}

#[test]
fn test_mkdir_no_parent() {
    new_ucmd!().arg(TEST_DIR5).fails();
}

#[test]
fn test_mkdir_dup_dir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR6).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR6).succeeds();
}

#[test]
fn test_mkdir_dup_file() {
    let scene = TestScenario::new(util_name!());
    scene.fixtures.touch(TEST_FILE7);
    scene.ucmd().arg(TEST_FILE7).fails();

    // mkdir should fail for a file even if -p is specified.
    scene.ucmd().arg("-p").arg(TEST_FILE7).fails();
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_mode() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("a=rwx").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40777);
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_alteration() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("-w").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40555);
}

#[test]
#[cfg(not(windows))]
fn test_multi_symbolic() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m")
        .arg("u=rwx,g=rx,o=")
        .arg(TEST_DIR1)
        .succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40750);
}

#[test]
#[cfg(not(windows))]
fn test_umask_compliance() {
    fn test_single_case(umask_set: mode_t) {
        let (at, mut ucmd) = at_and_ucmd!();

        let original_umask = unsafe { umask(umask_set) };

        ucmd.arg(TEST_DIR8).succeeds();
        let perms = at.metadata(TEST_DIR8).permissions().mode();

        assert_eq!(perms, (!umask_set & 0o0777) + 0o40000); // before compare, add the setguid, uid bits
        unsafe { umask(original_umask); } // set umask back to original
    }

    for i in 0o0..0o777 { // tests all permission combinations
        test_single_case(i);
    }

}
<commit_msg>test/mkdir: fixed assert types to both mode_t<commit_after>use crate::common::util::*;
#[cfg(not(windows))]
use std::os::unix::fs::PermissionsExt;
extern crate libc;
use self::libc::{umask, mode_t};

static TEST_DIR1: &str = "mkdir_test1";
static TEST_DIR2: &str = "mkdir_test2";
static TEST_DIR3: &str = "mkdir_test3";
static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &str = "mkdir_test6";
static TEST_FILE7: &str = "mkdir_test7";
static TEST_DIR8: &str = "mkdir_test8";

#[test]
fn test_mkdir_mkdir() {
        new_ucmd!().arg(TEST_DIR1).succeeds();
}

#[test]
fn test_mkdir_verbose() {
    let expected = "mkdir: created directory 'mkdir_test1'\n";
    new_ucmd!()
        .arg(TEST_DIR1)
        .arg("-v")
        .run()
        .stdout_is(expected);
}

#[test]
fn test_mkdir_dup_dir() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR2).succeeds();
    scene.ucmd().arg(TEST_DIR2).fails();
}

#[test]
fn test_mkdir_mode() {
    new_ucmd!().arg("-m").arg("755").arg(TEST_DIR3).succeeds();
}

#[test]
fn test_mkdir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parent").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parents").arg(TEST_DIR4).succeeds();
}

#[test]
fn test_mkdir_no_parent() {
    new_ucmd!().arg(TEST_DIR5).fails();
}

#[test]
fn test_mkdir_dup_dir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR6).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR6).succeeds();
}

#[test]
fn test_mkdir_dup_file() {
    let scene = TestScenario::new(util_name!());
    scene.fixtures.touch(TEST_FILE7);
    scene.ucmd().arg(TEST_FILE7).fails();

    // mkdir should fail for a file even if -p is specified.
    scene.ucmd().arg("-p").arg(TEST_FILE7).fails();
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_mode() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("a=rwx").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40777);
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_alteration() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("-w").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40555);
}

#[test]
#[cfg(not(windows))]
fn test_multi_symbolic() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m")
        .arg("u=rwx,g=rx,o=")
        .arg(TEST_DIR1)
        .succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40750);
}

#[test]
#[cfg(not(windows))]
fn test_umask_compliance() {
    fn test_single_case(umask_set: mode_t) {
        let (at, mut ucmd) = at_and_ucmd!();

        let original_umask = unsafe { umask(umask_set) };

        ucmd.arg(TEST_DIR8).succeeds();
        let perms = at.metadata(TEST_DIR8).permissions().mode() as mode_t;

        assert_eq!(perms, (!umask_set & 0o0777) + 0o40000); // before compare, add the setguid, uid bits
        unsafe { umask(original_umask); } // set umask back to original
    }

    for i in 0o0..0o777 { // tests all permission combinations
        test_single_case(i);
    }

}
<|endoftext|>


<commit_before>use crate::common::util::*;

#[test]
fn test_uname_compatible() {
    new_ucmd!().arg("-a").succeeds();
}

#[test]
fn test_uname_name() {
    new_ucmd!().arg("-n").succeeds();
}

#[test]
fn test_uname_processor() {
    let result = new_ucmd!().arg("-p").succeeds();
    assert_eq!(result.stdout_str().trim_end(), "unknown");
}

#[test]
fn test_uname_hardware_platform() {
    let result = new_ucmd!().arg("-i").succeeds();
    assert_eq!(result.stdout_str().trim_end(), "unknown");
}

#[test]
fn test_uname_machine() {
    new_ucmd!().arg("-m").succeeds();
}

#[test]
fn test_uname_kernel_version() {
    new_ucmd!().arg("-v").succeeds();
}

#[test]
fn test_uname_kernel() {
    let (_, mut ucmd) = at_and_ucmd!();

    #[cfg(target_os = "linux")]
    {
        let result = ucmd.arg("-o").succeeds();
        assert!(result.stdout_str().to_lowercase().contains("linux"));
    }

    #[cfg(not(target_os = "linux"))]
    ucmd.arg("-o").succeeds();
}
<commit_msg>tests/uname: add `--operating-system` test<commit_after>use crate::common::util::*;

#[test]
fn test_uname() {
    new_ucmd!().succeeds();
}

#[test]
fn test_uname_compatible() {
    new_ucmd!().arg("-a").succeeds();
}

#[test]
fn test_uname_name() {
    new_ucmd!().arg("-n").succeeds();
}

#[test]
fn test_uname_processor() {
    let result = new_ucmd!().arg("-p").succeeds();
    assert_eq!(result.stdout_str().trim_end(), "unknown");
}

#[test]
fn test_uname_hardware_platform() {
    let result = new_ucmd!().arg("-i").succeeds();
    assert_eq!(result.stdout_str().trim_end(), "unknown");
}

#[test]
fn test_uname_machine() {
    new_ucmd!().arg("-m").succeeds();
}

#[test]
fn test_uname_kernel_version() {
    new_ucmd!().arg("-v").succeeds();
}

#[test]
fn test_uname_kernel() {
    let (_, mut ucmd) = at_and_ucmd!();

    #[cfg(target_os = "linux")]
    {
        let result = ucmd.arg("-o").succeeds();
        assert!(result.stdout_str().to_lowercase().contains("linux"));
    }

    #[cfg(not(target_os = "linux"))]
    ucmd.arg("-o").succeeds();
}

#[test]
fn test_uname_operating_system() {
    #[cfg(target_vendor = "apple")]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("Darwin\n");
    #[cfg(target_os = "freebsd")]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("FreeBSD\n");
    #[cfg(target_os = "fuchsia")]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("Fuchsia\n");
    #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "")))]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("GNU/Linux\n");
    #[cfg(all(target_os = "linux", not(any(target_env = "gnu", target_env = ""))))]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("Linux\n");
    #[cfg(target_os = "netbsd")]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("NetBSD\n");
    #[cfg(target_os = "openbsd")]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("OpenBSD\n");
    #[cfg(target_os = "redox")]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("Redox\n");
    #[cfg(target_os = "windows")]
    new_ucmd!()
        .arg("--operating-system")
        .succeeds()
        .stdout_is("Windows NT\n");
}

#[test]
fn test_uname_help() {
    new_ucmd!()
        .arg("--help")
        .succeeds()
        .stdout_contains("system information");
}
<|endoftext|>


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

use crate::build_project::{build_project, commit_project, BuildProjectFailure};
use crate::config::Config;
use crate::errors::{Error, Result};
use crate::file_source::FileSource;
use crate::graphql_asts::GraphQLAsts;
use crate::red_to_green::RedToGreen;
use crate::{
    compiler_state::{ArtifactMapKind, CompilerState},
    file_source::FileSourceSubscriptionNextChange,
    FileSourceResult,
};
use common::{PerfLogEvent, PerfLogger};
use futures::future::join_all;
use graphql_watchman::WatchmanFileSourceSubscriptionNextChange;
use log::{debug, info};
use rayon::prelude::*;
use std::sync::Arc;
use tokio::{
    sync::Notify,
    task::{self, JoinHandle},
};

pub struct Compiler<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
}

impl<TPerfLogger: PerfLogger> Compiler<TPerfLogger> {
    pub fn new(config: Arc<Config>, perf_logger: Arc<TPerfLogger>) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");
        self.config.status_reporter.build_starts();
        let result: Result<CompilerState> = async {
            let file_source = FileSource::connect(&self.config, &setup_event).await?;
            let mut compiler_state = file_source
                .query(&setup_event, self.perf_logger.as_ref())
                .await?;

            self.build_projects(&mut compiler_state, &setup_event)
                .await?;

            Ok(compiler_state)
        }
        .await;
        setup_event.complete();

        match result {
            Ok(compiler_state) => {
                self.config.status_reporter.build_completes();
                Ok(compiler_state)
            }
            Err(error) => {
                self.config.status_reporter.build_errors(&error);
                Err(error)
            }
        }
    }

    pub async fn watch(&self) -> Result<()> {
        'watch: loop {
            let setup_event = self.perf_logger.create_event("compiler_setup");
            self.config.status_reporter.build_starts();
            let result: Result<(CompilerState, Arc<Notify>, JoinHandle<()>)> = async {
                let file_source = FileSource::connect(&self.config, &setup_event).await?;

                let (compiler_state, mut subscription) = file_source
                    .subscribe(&setup_event, self.perf_logger.as_ref())
                    .await?;

                let pending_file_source_changes =
                    compiler_state.pending_file_source_changes.clone();
                let source_control_update_status =
                    Arc::clone(&compiler_state.source_control_update_status);

                let notify_sender = Arc::new(Notify::new());
                let notify_receiver = notify_sender.clone();

                // First, set up watchman subscription
                let subscription_handle = task::spawn(async move {
                    loop {
                        let next_change = subscription.next_change().await;
                        match next_change {
                            Ok(FileSourceSubscriptionNextChange::Watchman(watchman_next_change)) => {
                                match watchman_next_change {
                                    WatchmanFileSourceSubscriptionNextChange::Result(file_source_changes) => {
                                        pending_file_source_changes
                                            .write()
                                            .unwrap()
                                            .push(FileSourceResult::Watchman(file_source_changes));
                                        notify_sender.notify_one();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateEnter => {
                                        info!("hg.update started...");
                                        source_control_update_status.mark_as_started();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateLeave => {
                                        info!("hg.update completed.");
                                        source_control_update_status.set_to_default();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdate => {
                                        info!("hg.update completed. Detected new base revision...");
                                        source_control_update_status.mark_as_completed();
                                        notify_sender.notify_one();
                                        break;
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::None => {}
                                }
                            }
                            Err(err) => {
                                panic!("Watchman subscription error: {}", err);
                            }
                        }
                    }
                });

                Ok((compiler_state, notify_receiver, subscription_handle))

            }
            .await;

            match result {
                Ok((mut compiler_state, notify_receiver, subscription_handle)) => {
                    let mut red_to_green = RedToGreen::new();
                    if let Err(err) = self.build_projects(&mut compiler_state, &setup_event).await {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                    }
                    setup_event.complete();
                    info!("Waiting for changes...");

                    self.incremental_build_loop(
                        compiler_state,
                        notify_receiver,
                        &subscription_handle,
                    )
                    .await;
                }
                Err(err) => {
                    self.config.status_reporter.build_errors(&err);
                    break 'watch Err(err);
                }
            }
        }
    }

    async fn incremental_build_loop(
        &self,
        mut compiler_state: CompilerState,
        notify_receiver: Arc<Notify>,
        subscription_handle: &JoinHandle<()>,
    ) {
        let mut red_to_green = RedToGreen::new();

        loop {
            notify_receiver.notified().await;

            if compiler_state.source_control_update_status.is_started() {
                continue;
            }

            if compiler_state.source_control_update_status.is_completed() {
                subscription_handle.abort();
                return;
            }

            // Single change to file sometimes produces 2 watchman change events for the same file
            // wait for 50ms in case there is a subsequent request
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;

            if compiler_state.has_pending_file_source_changes() {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                let had_new_changes = match compiler_state.merge_file_source_changes(
                    &self.config,
                    self.perf_logger.as_ref(),
                    false,
                ) {
                    Ok(b) => b,
                    Err(err) => {
                        let error_event = self.perf_logger.create_event("watch_build_error");
                        error_event.string("error", format!("Ignored Compilation Error: {}", err));
                        error_event.complete();
                        return;
                    }
                };

                if had_new_changes {
                    self.config.status_reporter.build_starts();
                    info!("Change detected, start compiling...");
                    if let Err(err) = self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                    {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                        red_to_green.clear_error_and_log(self.perf_logger.as_ref());
                    }
                    incremental_build_event.stop(incremental_build_time);
                    info!("Waiting for changes...");
                } else {
                    debug!("No new changes detected.");
                    incremental_build_event.stop(incremental_build_time);
                }
                incremental_build_event.complete();
            }
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let build_projects_time = setup_event.start("build_projects_time");
        let result = build_projects(
            Arc::clone(&self.config),
            Arc::clone(&self.perf_logger),
            setup_event,
            compiler_state,
        )
        .await;
        setup_event.stop(build_projects_time);
        setup_event.time("post_build_projects_time", || {
            result.and_then(|_| {
                compiler_state.complete_compilation();
                self.config.artifact_writer.finalize()?;
                if let Some(post_artifacts_write) = &self.config.post_artifacts_write {
                    post_artifacts_write(&self.config)
                        .map_err(|error| Error::PostArtifactsError { error })?;
                }

                Ok(())
            })
        })
    }
}

async fn build_projects<TPerfLogger: PerfLogger + 'static>(
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    setup_event: &impl PerfLogEvent,
    compiler_state: &mut CompilerState,
) -> Result<()> {
    let mut graphql_asts = setup_event.time("parse_sources_time", || {
        GraphQLAsts::from_graphql_sources_map(
            &compiler_state.graphql_sources,
            &compiler_state.get_dirty_definitions(&config),
        )
    })?;

    if compiler_state.should_cancel_current_build() {
        debug!("Build is cancelled: new file changes are pending.");
        return Err(Error::Cancelled);
    }

    let build_results: Vec<_> = config
        .par_enabled_projects()
        .filter(|project_config| {
            if let Some(base) = project_config.base {
                if compiler_state.project_has_pending_changes(base) {
                    return true;
                }
            }
            compiler_state.project_has_pending_changes(project_config.name)
        })
        .map(|project_config| {
            build_project(
                &config,
                project_config,
                compiler_state,
                &graphql_asts,
                Arc::clone(&perf_logger),
            )
        })
        .collect();
    let mut results = Vec::new();
    let mut errors = Vec::new();
    for result in build_results {
        match result {
            Ok(result) => results.push(result),
            Err(error) => match error {
                BuildProjectFailure::Error(error) => errors.push(error),
                BuildProjectFailure::Cancelled => {
                    return Err(Error::Cancelled);
                }
            },
        }
    }

    let mut build_cancelled_during_commit = false;
    if errors.is_empty() {
        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let mut handles: Vec<JoinHandle<std::result::Result<_, BuildProjectFailure>>> = Vec::new();
        for (project_name, schema, programs, artifacts) in results {
            let config = Arc::clone(&config);
            let perf_logger = Arc::clone(&perf_logger);
            let artifact_map = compiler_state
                .artifacts
                .get(&project_name)
                .cloned()
                .unwrap_or_else(|| Arc::new(ArtifactMapKind::Unconnected(Default::default())));
            let removed_definition_names = graphql_asts
                .remove(&project_name)
                .expect("Expect GraphQLAsts to exist.")
                .removed_definition_names;
            let dirty_artifact_paths = compiler_state
                .dirty_artifact_paths
                .get(&project_name)
                .cloned()
                .unwrap_or_default();

            let source_control_update_status =
                Arc::clone(&compiler_state.source_control_update_status);
            handles.push(task::spawn(async move {
                let project_config = &config.projects[&project_name];
                Ok((
                    project_name,
                    commit_project(
                        &config,
                        project_config,
                        perf_logger,
                        &schema,
                        programs,
                        artifacts,
                        artifact_map,
                        removed_definition_names,
                        dirty_artifact_paths,
                        source_control_update_status,
                    )
                    .await?,
                    schema,
                ))
            }));
        }

        for commit_result in join_all(handles).await {
            let commit_result: std::result::Result<std::result::Result<_, _>, _> = commit_result;
            let inner_result = commit_result.map_err(|e| Error::JoinError {
                error: e.to_string(),
            })?;
            match inner_result {
                Ok((project_name, next_artifact_map, schema)) => {
                    let next_artifact_map = Arc::new(ArtifactMapKind::Mapping(next_artifact_map));
                    compiler_state
                        .artifacts
                        .insert(project_name, next_artifact_map);
                    compiler_state.schema_cache.insert(project_name, schema);
                }
                Err(BuildProjectFailure::Error(error)) => {
                    errors.push(error);
                }
                Err(BuildProjectFailure::Cancelled) => {
                    build_cancelled_during_commit = true;
                }
            }
        }
    }

    if errors.is_empty() {
        match build_cancelled_during_commit {
            true => Err(Error::Cancelled),
            false => Ok(()),
        }
    } else {
        Err(Error::BuildProjectsErrors { errors })
    }
}
<commit_msg>Clarify that we are watching not waiting<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::build_project::{build_project, commit_project, BuildProjectFailure};
use crate::config::Config;
use crate::errors::{Error, Result};
use crate::file_source::FileSource;
use crate::graphql_asts::GraphQLAsts;
use crate::red_to_green::RedToGreen;
use crate::{
    compiler_state::{ArtifactMapKind, CompilerState},
    file_source::FileSourceSubscriptionNextChange,
    FileSourceResult,
};
use common::{PerfLogEvent, PerfLogger};
use futures::future::join_all;
use graphql_watchman::WatchmanFileSourceSubscriptionNextChange;
use log::{debug, info};
use rayon::prelude::*;
use std::sync::Arc;
use tokio::{
    sync::Notify,
    task::{self, JoinHandle},
};

pub struct Compiler<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
}

impl<TPerfLogger: PerfLogger> Compiler<TPerfLogger> {
    pub fn new(config: Arc<Config>, perf_logger: Arc<TPerfLogger>) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");
        self.config.status_reporter.build_starts();
        let result: Result<CompilerState> = async {
            let file_source = FileSource::connect(&self.config, &setup_event).await?;
            let mut compiler_state = file_source
                .query(&setup_event, self.perf_logger.as_ref())
                .await?;

            self.build_projects(&mut compiler_state, &setup_event)
                .await?;

            Ok(compiler_state)
        }
        .await;
        setup_event.complete();

        match result {
            Ok(compiler_state) => {
                self.config.status_reporter.build_completes();
                Ok(compiler_state)
            }
            Err(error) => {
                self.config.status_reporter.build_errors(&error);
                Err(error)
            }
        }
    }

    pub async fn watch(&self) -> Result<()> {
        'watch: loop {
            let setup_event = self.perf_logger.create_event("compiler_setup");
            self.config.status_reporter.build_starts();
            let result: Result<(CompilerState, Arc<Notify>, JoinHandle<()>)> = async {
                let file_source = FileSource::connect(&self.config, &setup_event).await?;

                let (compiler_state, mut subscription) = file_source
                    .subscribe(&setup_event, self.perf_logger.as_ref())
                    .await?;

                let pending_file_source_changes =
                    compiler_state.pending_file_source_changes.clone();
                let source_control_update_status =
                    Arc::clone(&compiler_state.source_control_update_status);

                let notify_sender = Arc::new(Notify::new());
                let notify_receiver = notify_sender.clone();

                // First, set up watchman subscription
                let subscription_handle = task::spawn(async move {
                    loop {
                        let next_change = subscription.next_change().await;
                        match next_change {
                            Ok(FileSourceSubscriptionNextChange::Watchman(watchman_next_change)) => {
                                match watchman_next_change {
                                    WatchmanFileSourceSubscriptionNextChange::Result(file_source_changes) => {
                                        pending_file_source_changes
                                            .write()
                                            .unwrap()
                                            .push(FileSourceResult::Watchman(file_source_changes));
                                        notify_sender.notify_one();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateEnter => {
                                        info!("hg.update started...");
                                        source_control_update_status.mark_as_started();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateLeave => {
                                        info!("hg.update completed.");
                                        source_control_update_status.set_to_default();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdate => {
                                        info!("hg.update completed. Detected new base revision...");
                                        source_control_update_status.mark_as_completed();
                                        notify_sender.notify_one();
                                        break;
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::None => {}
                                }
                            }
                            Err(err) => {
                                panic!("Watchman subscription error: {}", err);
                            }
                        }
                    }
                });

                Ok((compiler_state, notify_receiver, subscription_handle))

            }
            .await;

            match result {
                Ok((mut compiler_state, notify_receiver, subscription_handle)) => {
                    let mut red_to_green = RedToGreen::new();
                    if let Err(err) = self.build_projects(&mut compiler_state, &setup_event).await {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                    }
                    setup_event.complete();
                    info!("Waiting for changes...");

                    self.incremental_build_loop(
                        compiler_state,
                        notify_receiver,
                        &subscription_handle,
                    )
                    .await;
                }
                Err(err) => {
                    self.config.status_reporter.build_errors(&err);
                    break 'watch Err(err);
                }
            }
        }
    }

    async fn incremental_build_loop(
        &self,
        mut compiler_state: CompilerState,
        notify_receiver: Arc<Notify>,
        subscription_handle: &JoinHandle<()>,
    ) {
        let mut red_to_green = RedToGreen::new();

        loop {
            notify_receiver.notified().await;

            if compiler_state.source_control_update_status.is_started() {
                continue;
            }

            if compiler_state.source_control_update_status.is_completed() {
                subscription_handle.abort();
                return;
            }

            // Single change to file sometimes produces 2 watchman change events for the same file
            // wait for 50ms in case there is a subsequent request
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;

            if compiler_state.has_pending_file_source_changes() {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                let had_new_changes = match compiler_state.merge_file_source_changes(
                    &self.config,
                    self.perf_logger.as_ref(),
                    false,
                ) {
                    Ok(b) => b,
                    Err(err) => {
                        let error_event = self.perf_logger.create_event("watch_build_error");
                        error_event.string("error", format!("Ignored Compilation Error: {}", err));
                        error_event.complete();
                        return;
                    }
                };

                if had_new_changes {
                    self.config.status_reporter.build_starts();
                    info!("Change detected, start compiling...");
                    if let Err(err) = self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                    {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                        red_to_green.clear_error_and_log(self.perf_logger.as_ref());
                    }
                    incremental_build_event.stop(incremental_build_time);
                    info!("Compilation completed.");
                    info!("Watching for new changes...");
                } else {
                    debug!("No new changes detected.");
                    incremental_build_event.stop(incremental_build_time);
                }
                incremental_build_event.complete();
            }
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let build_projects_time = setup_event.start("build_projects_time");
        let result = build_projects(
            Arc::clone(&self.config),
            Arc::clone(&self.perf_logger),
            setup_event,
            compiler_state,
        )
        .await;
        setup_event.stop(build_projects_time);
        setup_event.time("post_build_projects_time", || {
            result.and_then(|_| {
                compiler_state.complete_compilation();
                self.config.artifact_writer.finalize()?;
                if let Some(post_artifacts_write) = &self.config.post_artifacts_write {
                    post_artifacts_write(&self.config)
                        .map_err(|error| Error::PostArtifactsError { error })?;
                }

                Ok(())
            })
        })
    }
}

async fn build_projects<TPerfLogger: PerfLogger + 'static>(
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    setup_event: &impl PerfLogEvent,
    compiler_state: &mut CompilerState,
) -> Result<()> {
    let mut graphql_asts = setup_event.time("parse_sources_time", || {
        GraphQLAsts::from_graphql_sources_map(
            &compiler_state.graphql_sources,
            &compiler_state.get_dirty_definitions(&config),
        )
    })?;

    if compiler_state.should_cancel_current_build() {
        debug!("Build is cancelled: new file changes are pending.");
        return Err(Error::Cancelled);
    }

    let build_results: Vec<_> = config
        .par_enabled_projects()
        .filter(|project_config| {
            if let Some(base) = project_config.base {
                if compiler_state.project_has_pending_changes(base) {
                    return true;
                }
            }
            compiler_state.project_has_pending_changes(project_config.name)
        })
        .map(|project_config| {
            build_project(
                &config,
                project_config,
                compiler_state,
                &graphql_asts,
                Arc::clone(&perf_logger),
            )
        })
        .collect();
    let mut results = Vec::new();
    let mut errors = Vec::new();
    for result in build_results {
        match result {
            Ok(result) => results.push(result),
            Err(error) => match error {
                BuildProjectFailure::Error(error) => errors.push(error),
                BuildProjectFailure::Cancelled => {
                    return Err(Error::Cancelled);
                }
            },
        }
    }

    let mut build_cancelled_during_commit = false;
    if errors.is_empty() {
        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let mut handles: Vec<JoinHandle<std::result::Result<_, BuildProjectFailure>>> = Vec::new();
        for (project_name, schema, programs, artifacts) in results {
            let config = Arc::clone(&config);
            let perf_logger = Arc::clone(&perf_logger);
            let artifact_map = compiler_state
                .artifacts
                .get(&project_name)
                .cloned()
                .unwrap_or_else(|| Arc::new(ArtifactMapKind::Unconnected(Default::default())));
            let removed_definition_names = graphql_asts
                .remove(&project_name)
                .expect("Expect GraphQLAsts to exist.")
                .removed_definition_names;
            let dirty_artifact_paths = compiler_state
                .dirty_artifact_paths
                .get(&project_name)
                .cloned()
                .unwrap_or_default();

            let source_control_update_status =
                Arc::clone(&compiler_state.source_control_update_status);
            handles.push(task::spawn(async move {
                let project_config = &config.projects[&project_name];
                Ok((
                    project_name,
                    commit_project(
                        &config,
                        project_config,
                        perf_logger,
                        &schema,
                        programs,
                        artifacts,
                        artifact_map,
                        removed_definition_names,
                        dirty_artifact_paths,
                        source_control_update_status,
                    )
                    .await?,
                    schema,
                ))
            }));
        }

        for commit_result in join_all(handles).await {
            let commit_result: std::result::Result<std::result::Result<_, _>, _> = commit_result;
            let inner_result = commit_result.map_err(|e| Error::JoinError {
                error: e.to_string(),
            })?;
            match inner_result {
                Ok((project_name, next_artifact_map, schema)) => {
                    let next_artifact_map = Arc::new(ArtifactMapKind::Mapping(next_artifact_map));
                    compiler_state
                        .artifacts
                        .insert(project_name, next_artifact_map);
                    compiler_state.schema_cache.insert(project_name, schema);
                }
                Err(BuildProjectFailure::Error(error)) => {
                    errors.push(error);
                }
                Err(BuildProjectFailure::Cancelled) => {
                    build_cancelled_during_commit = true;
                }
            }
        }
    }

    if errors.is_empty() {
        match build_cancelled_during_commit {
            true => Err(Error::Cancelled),
            false => Ok(()),
        }
    } else {
        Err(Error::BuildProjectsErrors { errors })
    }
}
<|endoftext|>


<commit_before>#[doc = "Converts the Rust AST to the rustdoc document model"];

import rustc::syntax::ast;

export from_srv, extract;

fn from_srv(
    srv: astsrv::srv,
    default_name: str
) -> doc::cratedoc {

    #[doc = "Use the AST service to create a document tree"];

    astsrv::exec(srv) {|ctxt|
        extract(ctxt.ast, default_name)
    }
}

fn extract(
    crate: @ast::crate,
    default_name: str
) -> doc::cratedoc {
    {
        topmod: top_moddoc_from_crate(crate, default_name),
    }
}

fn top_moddoc_from_crate(
    crate: @ast::crate,
    default_name: str
) -> doc::moddoc {
    moddoc_from_mod(crate.node.module, default_name, ast::crate_node_id)
}

fn mk_itemdoc(id: ast::node_id, name: ast::ident) -> doc::itemdoc {
    {
        id: id,
        name: name,
        path: [],
        brief: none,
        desc: none,
    }
}

fn moddoc_from_mod(
    module: ast::_mod,
    name: ast::ident,
    id: ast::node_id
) -> doc::moddoc {
    {
        item: mk_itemdoc(id, name),
        items: ~vec::filter_map(module.items) {|item|
            alt item.node {
              ast::item_mod(m) {
                some(doc::modtag(
                    moddoc_from_mod(m, item.ident, item.id)
                ))
              }
              ast::item_fn(decl, _, _) {
                some(doc::fntag(
                    fndoc_from_fn(decl, item.ident, item.id)
                ))
              }
              ast::item_const(_, _) {
                some(doc::consttag(
                    constdoc_from_const(item.ident, item.id)
                ))
              }
              ast::item_enum(variants, _) {
                some(doc::enumtag(
                    enumdoc_from_enum(item.ident, item.id, variants)
                ))
              }
              ast::item_res(decl, _, _, _, _) {
                some(doc::restag(
                    resdoc_from_resource(decl, item.ident, item.id)
                ))
              }
              ast::item_iface(_, methods) {
                some(doc::ifacetag(
                    ifacedoc_from_iface(methods, item.ident, item.id)
                ))
              }
              ast::item_impl(_, _, _, methods) {
                some(doc::impltag(
                    impldoc_from_impl(methods, item.ident, item.id)
                ))
              }
              ast::item_ty(_, _) {
                some(doc::tytag(
                    tydoc_from_ty(item.ident, item.id)
                ))
              }
              _ {
                none
              }
            }
        }
    }
}

fn fndoc_from_fn(
    decl: ast::fn_decl,
    name: ast::ident,
    id: ast::node_id
) -> doc::fndoc {
    {
        item: mk_itemdoc(id, name),
        args: argdocs_from_args(decl.inputs),
        return: {
            desc: none,
            ty: none
        },
        failure: none,
        sig: none
    }
}

#[test]
fn should_extract_fn_args() {
    let source = "fn a(b: int, c: int) { }";
    let ast = parse::from_str(source);
    let doc = extract(ast, "");
    let fn_ = doc.topmod.fns()[0];
    assert fn_.args[0].name == "b";
    assert fn_.args[1].name == "c";
}

fn argdocs_from_args(args: [ast::arg]) -> [doc::argdoc] {
    vec::map(args, argdoc_from_arg)
}

fn argdoc_from_arg(arg: ast::arg) -> doc::argdoc {
    {
        name: arg.ident,
        desc: none,
        ty: none
    }
}

fn constdoc_from_const(
    name: ast::ident,
    id: ast::node_id
) -> doc::constdoc {
    {
        item: mk_itemdoc(id, name),
        ty: none
    }
}

#[test]
fn should_extract_const_name_and_id() {
    let doc = test::mk_doc("const a: int = 0;");
    assert doc.topmod.consts()[0].id() != 0;
    assert doc.topmod.consts()[0].name() == "a";
}

fn enumdoc_from_enum(
    name: ast::ident,
    id: ast::node_id,
    variants: [ast::variant]
) -> doc::enumdoc {
    {
        item: mk_itemdoc(id, name),
        variants: variantdocs_from_variants(variants)
    }
}

fn variantdocs_from_variants(
    variants: [ast::variant]
) -> [doc::variantdoc] {
    vec::map(variants, variantdoc_from_variant)
}

fn variantdoc_from_variant(variant: ast::variant) -> doc::variantdoc {
    {
        name: variant.node.name,
        desc: none,
        sig: none
    }
}

#[test]
fn should_extract_enums() {
    let doc = test::mk_doc("enum e { v }");
    assert doc.topmod.enums()[0].id() != 0;
    assert doc.topmod.enums()[0].name() == "e";
}

#[test]
fn should_extract_enum_variants() {
    let doc = test::mk_doc("enum e { v }");
    assert doc.topmod.enums()[0].variants[0].name == "v";
}

fn resdoc_from_resource(
    decl: ast::fn_decl,
    name: str,
    id: ast::node_id
) -> doc::resdoc {
    {
        item: mk_itemdoc(id, name),
        args: argdocs_from_args(decl.inputs),
        sig: none
    }
}

#[test]
fn should_extract_resources() {
    let doc = test::mk_doc("resource r(b: bool) { }");
    assert doc.topmod.resources()[0].id() != 0;
    assert doc.topmod.resources()[0].name() == "r";
}

#[test]
fn should_extract_resource_args() {
    let doc = test::mk_doc("resource r(b: bool) { }");
    assert doc.topmod.resources()[0].args[0].name == "b";
}

fn ifacedoc_from_iface(
    methods: [ast::ty_method],
    name: str,
    id: ast::node_id
) -> doc::ifacedoc {
    {
        item: mk_itemdoc(id, name),
        methods: vec::map(methods) {|method|
            {
                name: method.ident,
                brief: none,
                desc: none,
                args: argdocs_from_args(method.decl.inputs),
                return: {
                    desc: none,
                    ty: none
                },
                failure: none,
                sig: none
            }
        }
    }
}

#[test]
fn should_extract_ifaces() {
    let doc = test::mk_doc("iface i { fn f(); }");
    assert doc.topmod.ifaces()[0].name() == "i";
}

#[test]
fn should_extract_iface_methods() {
    let doc = test::mk_doc("iface i { fn f(); }");
    assert doc.topmod.ifaces()[0].methods[0].name == "f";
}

#[test]
fn should_extract_iface_method_args() {
    let doc = test::mk_doc("iface i { fn f(a: bool); }");
    assert doc.topmod.ifaces()[0].methods[0].args[0].name == "a";
}

fn impldoc_from_impl(
    methods: [@ast::method],
    name: str,
    id: ast::node_id
) -> doc::impldoc {
    {
        item: mk_itemdoc(id, name),
        iface_ty: none,
        self_ty: none,
        methods: vec::map(methods) {|method|
            {
                name: method.ident,
                brief: none,
                desc: none,
                args: argdocs_from_args(method.decl.inputs),
                return: {
                    desc: none,
                    ty: none
                },
                failure: none,
                sig: none
            }
        }
    }
}

#[test]
fn should_extract_impls_with_names() {
    let doc = test::mk_doc("impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].name() == "i";
}

#[test]
fn should_extract_impls_without_names() {
    let doc = test::mk_doc("impl of i for int { fn a() { } }");
    assert doc.topmod.impls()[0].name() == "i";
}

#[test]
fn should_extract_impl_methods() {
    let doc = test::mk_doc("impl i for int { fn f() { } }");
    assert doc.topmod.impls()[0].methods[0].name == "f";
}

#[test]
fn should_extract_impl_method_args() {
    let doc = test::mk_doc("impl i for int { fn f(a: bool) { } }");
    assert doc.topmod.impls()[0].methods[0].args[0].name == "a";
}

fn tydoc_from_ty(
    name: str,
    id: ast::node_id
) -> doc::tydoc {
    {
        item: mk_itemdoc(id, name),
        sig: none
    }
}

#[test]
fn should_extract_tys() {
    let doc = test::mk_doc("type a = int;");
    assert doc.topmod.types()[0].name() == "a";
}

#[cfg(test)]
mod test {

    fn mk_doc(source: str) -> doc::cratedoc {
        let ast = parse::from_str(source);
        extract(ast, "")
    }

    #[test]
    fn extract_empty_crate() {
        let doc = mk_doc("");
        assert vec::is_empty(doc.topmod.mods());
        assert vec::is_empty(doc.topmod.fns());
    }

    #[test]
    fn extract_mods() {
        let doc = mk_doc("mod a { mod b { } mod c { } }");
        assert doc.topmod.mods()[0].name() == "a";
        assert doc.topmod.mods()[0].mods()[0].name() == "b";
        assert doc.topmod.mods()[0].mods()[1].name() == "c";
    }

    #[test]
    fn extract_mods_deep() {
        let doc = mk_doc("mod a { mod b { mod c { } } }");
        assert doc.topmod.mods()[0].mods()[0].mods()[0].name() == "c";
    }

    #[test]
    fn extract_should_set_mod_ast_id() {
        let doc = mk_doc("mod a { }");
        assert doc.topmod.mods()[0].id() != 0;
    }

    #[test]
    fn extract_fns() {
        let doc = mk_doc(
            "fn a() { } \
             mod b { fn c() { } }");
        assert doc.topmod.fns()[0].name() == "a";
        assert doc.topmod.mods()[0].fns()[0].name() == "c";
    }

    #[test]
    fn extract_should_set_fn_ast_id() {
        let doc = mk_doc("fn a() { }");
        assert doc.topmod.fns()[0].id() != 0;
    }

    #[test]
    fn extract_should_use_default_crate_name() {
        let source = "";
        let ast = parse::from_str(source);
        let doc = extract(ast, "burp");
        assert doc.topmod.name() == "burp";
    }

    #[test]
    fn extract_from_seq_srv() {
        let source = "";
        let srv = astsrv::mk_srv_from_str(source);
        let doc = from_srv(srv, "name");
        assert doc.topmod.name() == "name";
    }
}<commit_msg>rustdoc: Refactor itemdoc creation in extract pass<commit_after>#[doc = "Converts the Rust AST to the rustdoc document model"];

import rustc::syntax::ast;

export from_srv, extract;

fn from_srv(
    srv: astsrv::srv,
    default_name: str
) -> doc::cratedoc {

    #[doc = "Use the AST service to create a document tree"];

    astsrv::exec(srv) {|ctxt|
        extract(ctxt.ast, default_name)
    }
}

fn extract(
    crate: @ast::crate,
    default_name: str
) -> doc::cratedoc {
    {
        topmod: top_moddoc_from_crate(crate, default_name),
    }
}

fn top_moddoc_from_crate(
    crate: @ast::crate,
    default_name: str
) -> doc::moddoc {
    moddoc_from_mod(mk_itemdoc(ast::crate_node_id, default_name),
                    crate.node.module)
}

fn mk_itemdoc(id: ast::node_id, name: ast::ident) -> doc::itemdoc {
    {
        id: id,
        name: name,
        path: [],
        brief: none,
        desc: none,
    }
}

fn moddoc_from_mod(
    itemdoc: doc::itemdoc,
    module: ast::_mod
) -> doc::moddoc {
    {
        item: itemdoc,
        items: ~vec::filter_map(module.items) {|item|
            let itemdoc = mk_itemdoc(item.id, item.ident);
            alt item.node {
              ast::item_mod(m) {
                some(doc::modtag(
                    moddoc_from_mod(itemdoc, m)
                ))
              }
              ast::item_fn(decl, _, _) {
                some(doc::fntag(
                    fndoc_from_fn(itemdoc, decl)
                ))
              }
              ast::item_const(_, _) {
                some(doc::consttag(
                    constdoc_from_const(itemdoc)
                ))
              }
              ast::item_enum(variants, _) {
                some(doc::enumtag(
                    enumdoc_from_enum(itemdoc, variants)
                ))
              }
              ast::item_res(decl, _, _, _, _) {
                some(doc::restag(
                    resdoc_from_resource(itemdoc, decl)
                ))
              }
              ast::item_iface(_, methods) {
                some(doc::ifacetag(
                    ifacedoc_from_iface(itemdoc, methods)
                ))
              }
              ast::item_impl(_, _, _, methods) {
                some(doc::impltag(
                    impldoc_from_impl(itemdoc, methods)
                ))
              }
              ast::item_ty(_, _) {
                some(doc::tytag(
                    tydoc_from_ty(itemdoc)
                ))
              }
              _ {
                none
              }
            }
        }
    }
}

fn fndoc_from_fn(
    itemdoc: doc::itemdoc,
    decl: ast::fn_decl
) -> doc::fndoc {
    {
        item: itemdoc,
        args: argdocs_from_args(decl.inputs),
        return: {
            desc: none,
            ty: none
        },
        failure: none,
        sig: none
    }
}

#[test]
fn should_extract_fn_args() {
    let source = "fn a(b: int, c: int) { }";
    let ast = parse::from_str(source);
    let doc = extract(ast, "");
    let fn_ = doc.topmod.fns()[0];
    assert fn_.args[0].name == "b";
    assert fn_.args[1].name == "c";
}

fn argdocs_from_args(args: [ast::arg]) -> [doc::argdoc] {
    vec::map(args, argdoc_from_arg)
}

fn argdoc_from_arg(arg: ast::arg) -> doc::argdoc {
    {
        name: arg.ident,
        desc: none,
        ty: none
    }
}

fn constdoc_from_const(itemdoc: doc::itemdoc) -> doc::constdoc {
    {
        item: itemdoc,
        ty: none
    }
}

#[test]
fn should_extract_const_name_and_id() {
    let doc = test::mk_doc("const a: int = 0;");
    assert doc.topmod.consts()[0].id() != 0;
    assert doc.topmod.consts()[0].name() == "a";
}

fn enumdoc_from_enum(
    itemdoc: doc::itemdoc,
    variants: [ast::variant]
) -> doc::enumdoc {
    {
        item: itemdoc,
        variants: variantdocs_from_variants(variants)
    }
}

fn variantdocs_from_variants(
    variants: [ast::variant]
) -> [doc::variantdoc] {
    vec::map(variants, variantdoc_from_variant)
}

fn variantdoc_from_variant(variant: ast::variant) -> doc::variantdoc {
    {
        name: variant.node.name,
        desc: none,
        sig: none
    }
}

#[test]
fn should_extract_enums() {
    let doc = test::mk_doc("enum e { v }");
    assert doc.topmod.enums()[0].id() != 0;
    assert doc.topmod.enums()[0].name() == "e";
}

#[test]
fn should_extract_enum_variants() {
    let doc = test::mk_doc("enum e { v }");
    assert doc.topmod.enums()[0].variants[0].name == "v";
}

fn resdoc_from_resource(
    itemdoc: doc::itemdoc,
    decl: ast::fn_decl
) -> doc::resdoc {
    {
        item: itemdoc,
        args: argdocs_from_args(decl.inputs),
        sig: none
    }
}

#[test]
fn should_extract_resources() {
    let doc = test::mk_doc("resource r(b: bool) { }");
    assert doc.topmod.resources()[0].id() != 0;
    assert doc.topmod.resources()[0].name() == "r";
}

#[test]
fn should_extract_resource_args() {
    let doc = test::mk_doc("resource r(b: bool) { }");
    assert doc.topmod.resources()[0].args[0].name == "b";
}

fn ifacedoc_from_iface(
    itemdoc: doc::itemdoc,
    methods: [ast::ty_method]
) -> doc::ifacedoc {
    {
        item: itemdoc,
        methods: vec::map(methods) {|method|
            {
                name: method.ident,
                brief: none,
                desc: none,
                args: argdocs_from_args(method.decl.inputs),
                return: {
                    desc: none,
                    ty: none
                },
                failure: none,
                sig: none
            }
        }
    }
}

#[test]
fn should_extract_ifaces() {
    let doc = test::mk_doc("iface i { fn f(); }");
    assert doc.topmod.ifaces()[0].name() == "i";
}

#[test]
fn should_extract_iface_methods() {
    let doc = test::mk_doc("iface i { fn f(); }");
    assert doc.topmod.ifaces()[0].methods[0].name == "f";
}

#[test]
fn should_extract_iface_method_args() {
    let doc = test::mk_doc("iface i { fn f(a: bool); }");
    assert doc.topmod.ifaces()[0].methods[0].args[0].name == "a";
}

fn impldoc_from_impl(
    itemdoc: doc::itemdoc,
    methods: [@ast::method]
) -> doc::impldoc {
    {
        item: itemdoc,
        iface_ty: none,
        self_ty: none,
        methods: vec::map(methods) {|method|
            {
                name: method.ident,
                brief: none,
                desc: none,
                args: argdocs_from_args(method.decl.inputs),
                return: {
                    desc: none,
                    ty: none
                },
                failure: none,
                sig: none
            }
        }
    }
}

#[test]
fn should_extract_impls_with_names() {
    let doc = test::mk_doc("impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].name() == "i";
}

#[test]
fn should_extract_impls_without_names() {
    let doc = test::mk_doc("impl of i for int { fn a() { } }");
    assert doc.topmod.impls()[0].name() == "i";
}

#[test]
fn should_extract_impl_methods() {
    let doc = test::mk_doc("impl i for int { fn f() { } }");
    assert doc.topmod.impls()[0].methods[0].name == "f";
}

#[test]
fn should_extract_impl_method_args() {
    let doc = test::mk_doc("impl i for int { fn f(a: bool) { } }");
    assert doc.topmod.impls()[0].methods[0].args[0].name == "a";
}

fn tydoc_from_ty(
    itemdoc: doc::itemdoc
) -> doc::tydoc {
    {
        item: itemdoc,
        sig: none
    }
}

#[test]
fn should_extract_tys() {
    let doc = test::mk_doc("type a = int;");
    assert doc.topmod.types()[0].name() == "a";
}

#[cfg(test)]
mod test {

    fn mk_doc(source: str) -> doc::cratedoc {
        let ast = parse::from_str(source);
        extract(ast, "")
    }

    #[test]
    fn extract_empty_crate() {
        let doc = mk_doc("");
        assert vec::is_empty(doc.topmod.mods());
        assert vec::is_empty(doc.topmod.fns());
    }

    #[test]
    fn extract_mods() {
        let doc = mk_doc("mod a { mod b { } mod c { } }");
        assert doc.topmod.mods()[0].name() == "a";
        assert doc.topmod.mods()[0].mods()[0].name() == "b";
        assert doc.topmod.mods()[0].mods()[1].name() == "c";
    }

    #[test]
    fn extract_mods_deep() {
        let doc = mk_doc("mod a { mod b { mod c { } } }");
        assert doc.topmod.mods()[0].mods()[0].mods()[0].name() == "c";
    }

    #[test]
    fn extract_should_set_mod_ast_id() {
        let doc = mk_doc("mod a { }");
        assert doc.topmod.mods()[0].id() != 0;
    }

    #[test]
    fn extract_fns() {
        let doc = mk_doc(
            "fn a() { } \
             mod b { fn c() { } }");
        assert doc.topmod.fns()[0].name() == "a";
        assert doc.topmod.mods()[0].fns()[0].name() == "c";
    }

    #[test]
    fn extract_should_set_fn_ast_id() {
        let doc = mk_doc("fn a() { }");
        assert doc.topmod.fns()[0].id() != 0;
    }

    #[test]
    fn extract_should_use_default_crate_name() {
        let source = "";
        let ast = parse::from_str(source);
        let doc = extract(ast, "burp");
        assert doc.topmod.name() == "burp";
    }

    #[test]
    fn extract_from_seq_srv() {
        let source = "";
        let srv = astsrv::mk_srv_from_str(source);
        let doc = from_srv(srv, "name");
        assert doc.topmod.name() == "name";
    }
}<|endoftext|>


<commit_before>mod _impl;
mod checks;
mod suggestions;

pub use _impl::*;
pub use checks::*;
pub use suggestions::*;

use crate::astconv::AstConv;
use crate::check::coercion::DynamicCoerceMany;
use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};

use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::{self, Span};
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};

use std::cell::{Cell, RefCell};
use std::ops::Deref;

pub struct FnCtxt<'a, 'tcx> {
    pub(super) body_id: hir::HirId,

    /// The parameter environment used for proving trait obligations
    /// in this function. This can change when we descend into
    /// closures (as they bring new things into scope), hence it is
    /// not part of `Inherited` (as of the time of this writing,
    /// closures do not yet change the environment, but they will
    /// eventually).
    pub(super) param_env: ty::ParamEnv<'tcx>,

    /// Number of errors that had been reported when we started
    /// checking this function. On exit, if we find that *more* errors
    /// have been reported, we will skip regionck and other work that
    /// expects the types within the function to be consistent.
    // FIXME(matthewjasper) This should not exist, and it's not correct
    // if type checking is run in parallel.
    err_count_on_creation: usize,

    /// If `Some`, this stores coercion information for returned
    /// expressions. If `None`, this is in a context where return is
    /// inappropriate, such as a const expression.
    ///
    /// This is a `RefCell<DynamicCoerceMany>`, which means that we
    /// can track all the return expressions and then use them to
    /// compute a useful coercion from the set, similar to a match
    /// expression or other branching context. You can use methods
    /// like `expected_ty` to access the declared return type (if
    /// any).
    pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,

    pub(super) ret_coercion_impl_trait: Option<Ty<'tcx>>,

    pub(super) ret_type_span: Option<Span>,

    /// Used exclusively to reduce cost of advanced evaluation used for
    /// more helpful diagnostics.
    pub(super) in_tail_expr: bool,

    /// First span of a return site that we find. Used in error messages.
    pub(super) ret_coercion_span: RefCell<Option<Span>>,

    pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,

    pub(super) ps: RefCell<UnsafetyState>,

    /// Whether the last checked node generates a divergence (e.g.,
    /// `return` will set this to `Always`). In general, when entering
    /// an expression or other node in the tree, the initial value
    /// indicates whether prior parts of the containing expression may
    /// have diverged. It is then typically set to `Maybe` (and the
    /// old value remembered) for processing the subparts of the
    /// current expression. As each subpart is processed, they may set
    /// the flag to `Always`, etc. Finally, at the end, we take the
    /// result and "union" it with the original value, so that when we
    /// return the flag indicates if any subpart of the parent
    /// expression (up to and including this part) has diverged. So,
    /// if you read it after evaluating a subexpression `X`, the value
    /// you get indicates whether any subexpression that was
    /// evaluating up to and including `X` diverged.
    ///
    /// We currently use this flag only for diagnostic purposes:
    ///
    /// - To warn about unreachable code: if, after processing a
    ///   sub-expression but before we have applied the effects of the
    ///   current node, we see that the flag is set to `Always`, we
    ///   can issue a warning. This corresponds to something like
    ///   `foo(return)`; we warn on the `foo()` expression. (We then
    ///   update the flag to `WarnedAlways` to suppress duplicate
    ///   reports.) Similarly, if we traverse to a fresh statement (or
    ///   tail expression) from a `Always` setting, we will issue a
    ///   warning. This corresponds to something like `{return;
    ///   foo();}` or `{return; 22}`, where we would warn on the
    ///   `foo()` or `22`.
    ///
    /// An expression represents dead code if, after checking it,
    /// the diverges flag is set to something other than `Maybe`.
    pub(super) diverges: Cell<Diverges>,

    /// Whether any child nodes have any type errors.
    pub(super) has_errors: Cell<bool>,

    pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,

    pub(super) inh: &'a Inherited<'a, 'tcx>,
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    pub fn new(
        inh: &'a Inherited<'a, 'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        body_id: hir::HirId,
    ) -> FnCtxt<'a, 'tcx> {
        FnCtxt {
            body_id,
            param_env,
            err_count_on_creation: inh.tcx.sess.err_count(),
            ret_coercion: None,
            ret_coercion_impl_trait: None,
            ret_type_span: None,
            in_tail_expr: false,
            ret_coercion_span: RefCell::new(None),
            resume_yield_tys: None,
            ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
            diverges: Cell::new(Diverges::Maybe),
            has_errors: Cell::new(false),
            enclosing_breakables: RefCell::new(EnclosingBreakables {
                stack: Vec::new(),
                by_id: Default::default(),
            }),
            inh,
        }
    }

    pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
        ObligationCause::new(span, self.body_id, code)
    }

    pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
        self.cause(span, ObligationCauseCode::MiscObligation)
    }

    pub fn sess(&self) -> &Session {
        &self.tcx.sess
    }

    pub fn errors_reported_since_creation(&self) -> bool {
        self.tcx.sess.err_count() > self.err_count_on_creation
    }
}

impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
    type Target = Inherited<'a, 'tcx>;
    fn deref(&self) -> &Self::Target {
        &self.inh
    }
}

impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
        self.tcx
    }

    fn item_def_id(&self) -> Option<DefId> {
        None
    }

    fn default_constness_for_trait_bounds(&self) -> hir::Constness {
        // FIXME: refactor this into a method
        let node = self.tcx.hir().get(self.body_id);
        if let Some(fn_like) = FnLikeNode::from_node(node) {
            fn_like.constness()
        } else {
            hir::Constness::NotConst
        }
    }

    fn get_type_parameter_bounds(
        &self,
        _: Span,
        def_id: DefId,
        assoc_name: Ident,
    ) -> ty::GenericPredicates<'tcx> {
        let tcx = self.tcx;
        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
        let item_id = tcx.hir().ty_param_owner(hir_id);
        let item_def_id = tcx.hir().local_def_id(item_id);
        let generics = tcx.generics_of(item_def_id);
        let index = generics.param_def_id_to_index[&def_id];
        ty::GenericPredicates {
            parent: None,
            predicates: tcx.arena.alloc_from_iter(
                self.param_env.caller_bounds().iter().filter_map(|predicate| {
                    match predicate.skip_binders() {
                        ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => {
                            let trait_did = data.def_id();
                            if tcx
                                .associated_items(trait_did)
                                .find_by_name_and_kind(
                                    tcx,
                                    assoc_name,
                                    ty::AssocKind::Type,
                                    trait_did,
                                )
                                .is_some()
                            {
                                // HACK(eddyb) should get the original `Span`.
                                let span = tcx.def_span(def_id);
                                Some((predicate, span))
                            } else {
                                None
                            }
                        }
                        _ => None,
                    }
                }),
            ),
        }
    }

    fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
        let v = match def {
            Some(def) => infer::EarlyBoundRegion(span, def.name),
            None => infer::MiscVariable(span),
        };
        Some(self.next_region_var(v))
    }

    fn allow_ty_infer(&self) -> bool {
        true
    }

    fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
        if let Some(param) = param {
            if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
                return ty;
            }
            unreachable!()
        } else {
            self.next_ty_var(TypeVariableOrigin {
                kind: TypeVariableOriginKind::TypeInference,
                span,
            })
        }
    }

    fn ct_infer(
        &self,
        ty: Ty<'tcx>,
        param: Option<&ty::GenericParamDef>,
        span: Span,
    ) -> &'tcx Const<'tcx> {
        if let Some(param) = param {
            if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
                return ct;
            }
            unreachable!()
        } else {
            self.next_const_var(
                ty,
                ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
            )
        }
    }

    fn projected_ty_from_poly_trait_ref(
        &self,
        span: Span,
        item_def_id: DefId,
        item_segment: &hir::PathSegment<'_>,
        poly_trait_ref: ty::PolyTraitRef<'tcx>,
    ) -> Ty<'tcx> {
        let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
            span,
            infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
            poly_trait_ref,
        );

        let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
            self,
            self.tcx,
            span,
            item_def_id,
            item_segment,
            trait_ref.substs,
        );

        self.tcx().mk_projection(item_def_id, item_substs)
    }

    fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
        if ty.has_escaping_bound_vars() {
            ty // FIXME: normalization and escaping regions
        } else {
            self.normalize_associated_types_in(span, &ty)
        }
    }

    fn set_tainted_by_errors(&self) {
        self.infcx.set_tainted_by_errors()
    }

    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
        self.write_ty(hir_id, ty)
    }
}
<commit_msg>Remove unneeded logic<commit_after>mod _impl;
mod checks;
mod suggestions;

pub use _impl::*;
pub use checks::*;
pub use suggestions::*;

use crate::astconv::AstConv;
use crate::check::coercion::DynamicCoerceMany;
use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};

use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::{self, Span};
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};

use std::cell::{Cell, RefCell};
use std::ops::Deref;

pub struct FnCtxt<'a, 'tcx> {
    pub(super) body_id: hir::HirId,

    /// The parameter environment used for proving trait obligations
    /// in this function. This can change when we descend into
    /// closures (as they bring new things into scope), hence it is
    /// not part of `Inherited` (as of the time of this writing,
    /// closures do not yet change the environment, but they will
    /// eventually).
    pub(super) param_env: ty::ParamEnv<'tcx>,

    /// Number of errors that had been reported when we started
    /// checking this function. On exit, if we find that *more* errors
    /// have been reported, we will skip regionck and other work that
    /// expects the types within the function to be consistent.
    // FIXME(matthewjasper) This should not exist, and it's not correct
    // if type checking is run in parallel.
    err_count_on_creation: usize,

    /// If `Some`, this stores coercion information for returned
    /// expressions. If `None`, this is in a context where return is
    /// inappropriate, such as a const expression.
    ///
    /// This is a `RefCell<DynamicCoerceMany>`, which means that we
    /// can track all the return expressions and then use them to
    /// compute a useful coercion from the set, similar to a match
    /// expression or other branching context. You can use methods
    /// like `expected_ty` to access the declared return type (if
    /// any).
    pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,

    pub(super) ret_coercion_impl_trait: Option<Ty<'tcx>>,

    pub(super) ret_type_span: Option<Span>,

    /// Used exclusively to reduce cost of advanced evaluation used for
    /// more helpful diagnostics.
    pub(super) in_tail_expr: bool,

    /// First span of a return site that we find. Used in error messages.
    pub(super) ret_coercion_span: RefCell<Option<Span>>,

    pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,

    pub(super) ps: RefCell<UnsafetyState>,

    /// Whether the last checked node generates a divergence (e.g.,
    /// `return` will set this to `Always`). In general, when entering
    /// an expression or other node in the tree, the initial value
    /// indicates whether prior parts of the containing expression may
    /// have diverged. It is then typically set to `Maybe` (and the
    /// old value remembered) for processing the subparts of the
    /// current expression. As each subpart is processed, they may set
    /// the flag to `Always`, etc. Finally, at the end, we take the
    /// result and "union" it with the original value, so that when we
    /// return the flag indicates if any subpart of the parent
    /// expression (up to and including this part) has diverged. So,
    /// if you read it after evaluating a subexpression `X`, the value
    /// you get indicates whether any subexpression that was
    /// evaluating up to and including `X` diverged.
    ///
    /// We currently use this flag only for diagnostic purposes:
    ///
    /// - To warn about unreachable code: if, after processing a
    ///   sub-expression but before we have applied the effects of the
    ///   current node, we see that the flag is set to `Always`, we
    ///   can issue a warning. This corresponds to something like
    ///   `foo(return)`; we warn on the `foo()` expression. (We then
    ///   update the flag to `WarnedAlways` to suppress duplicate
    ///   reports.) Similarly, if we traverse to a fresh statement (or
    ///   tail expression) from a `Always` setting, we will issue a
    ///   warning. This corresponds to something like `{return;
    ///   foo();}` or `{return; 22}`, where we would warn on the
    ///   `foo()` or `22`.
    ///
    /// An expression represents dead code if, after checking it,
    /// the diverges flag is set to something other than `Maybe`.
    pub(super) diverges: Cell<Diverges>,

    /// Whether any child nodes have any type errors.
    pub(super) has_errors: Cell<bool>,

    pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,

    pub(super) inh: &'a Inherited<'a, 'tcx>,
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    pub fn new(
        inh: &'a Inherited<'a, 'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        body_id: hir::HirId,
    ) -> FnCtxt<'a, 'tcx> {
        FnCtxt {
            body_id,
            param_env,
            err_count_on_creation: inh.tcx.sess.err_count(),
            ret_coercion: None,
            ret_coercion_impl_trait: None,
            ret_type_span: None,
            in_tail_expr: false,
            ret_coercion_span: RefCell::new(None),
            resume_yield_tys: None,
            ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
            diverges: Cell::new(Diverges::Maybe),
            has_errors: Cell::new(false),
            enclosing_breakables: RefCell::new(EnclosingBreakables {
                stack: Vec::new(),
                by_id: Default::default(),
            }),
            inh,
        }
    }

    pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
        ObligationCause::new(span, self.body_id, code)
    }

    pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
        self.cause(span, ObligationCauseCode::MiscObligation)
    }

    pub fn sess(&self) -> &Session {
        &self.tcx.sess
    }

    pub fn errors_reported_since_creation(&self) -> bool {
        self.tcx.sess.err_count() > self.err_count_on_creation
    }
}

impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
    type Target = Inherited<'a, 'tcx>;
    fn deref(&self) -> &Self::Target {
        &self.inh
    }
}

impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
        self.tcx
    }

    fn item_def_id(&self) -> Option<DefId> {
        None
    }

    fn default_constness_for_trait_bounds(&self) -> hir::Constness {
        // FIXME: refactor this into a method
        let node = self.tcx.hir().get(self.body_id);
        if let Some(fn_like) = FnLikeNode::from_node(node) {
            fn_like.constness()
        } else {
            hir::Constness::NotConst
        }
    }

    fn get_type_parameter_bounds(
        &self,
        _: Span,
        def_id: DefId,
        _: Ident,
    ) -> ty::GenericPredicates<'tcx> {
        let tcx = self.tcx;
        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
        let item_id = tcx.hir().ty_param_owner(hir_id);
        let item_def_id = tcx.hir().local_def_id(item_id);
        let generics = tcx.generics_of(item_def_id);
        let index = generics.param_def_id_to_index[&def_id];
        ty::GenericPredicates {
            parent: None,
            predicates: tcx.arena.alloc_from_iter(
                self.param_env.caller_bounds().iter().filter_map(|predicate| {
                    match predicate.skip_binders() {
                        ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => {
                            // HACK(eddyb) should get the original `Span`.
                            let span = tcx.def_span(def_id);
                            Some((predicate, span))
                        }
                        _ => None,
                    }
                }),
            ),
        }
    }

    fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
        let v = match def {
            Some(def) => infer::EarlyBoundRegion(span, def.name),
            None => infer::MiscVariable(span),
        };
        Some(self.next_region_var(v))
    }

    fn allow_ty_infer(&self) -> bool {
        true
    }

    fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
        if let Some(param) = param {
            if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
                return ty;
            }
            unreachable!()
        } else {
            self.next_ty_var(TypeVariableOrigin {
                kind: TypeVariableOriginKind::TypeInference,
                span,
            })
        }
    }

    fn ct_infer(
        &self,
        ty: Ty<'tcx>,
        param: Option<&ty::GenericParamDef>,
        span: Span,
    ) -> &'tcx Const<'tcx> {
        if let Some(param) = param {
            if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
                return ct;
            }
            unreachable!()
        } else {
            self.next_const_var(
                ty,
                ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
            )
        }
    }

    fn projected_ty_from_poly_trait_ref(
        &self,
        span: Span,
        item_def_id: DefId,
        item_segment: &hir::PathSegment<'_>,
        poly_trait_ref: ty::PolyTraitRef<'tcx>,
    ) -> Ty<'tcx> {
        let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
            span,
            infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
            poly_trait_ref,
        );

        let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
            self,
            self.tcx,
            span,
            item_def_id,
            item_segment,
            trait_ref.substs,
        );

        self.tcx().mk_projection(item_def_id, item_substs)
    }

    fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
        if ty.has_escaping_bound_vars() {
            ty // FIXME: normalization and escaping regions
        } else {
            self.normalize_associated_types_in(span, &ty)
        }
    }

    fn set_tainted_by_errors(&self) {
        self.infcx.set_tainted_by_errors()
    }

    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
        self.write_ty(hir_id, ty)
    }
}
<|endoftext|>