<commit_before>//! Code for building the standard library.

use crate::core::compiler::{BuildContext, CompileMode, Kind, Unit};
use crate::core::profiles::UnitFor;
use crate::core::resolver::ResolveOpts;
use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspace};
use crate::ops::{self, Packages};
use crate::util::errors::CargoResult;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

/// Parse the `-Zbuild-std` flag.
pub fn parse_unstable_flag(value: Option<&str>) -> Vec<String> {
    // This is a temporary hack until there is a more principled way to
    // declare dependencies in Cargo.toml.
    let value = value.unwrap_or("std");
    let mut crates: HashSet<&str> = value.split(',').collect();
    if crates.contains("std") {
        crates.insert("core");
        crates.insert("panic_unwind");
        crates.insert("compiler_builtins");
    } else if crates.contains("core") {
        crates.insert("compiler_builtins");
    }
    crates.into_iter().map(|s| s.to_string()).collect()
}

/// Resolve the standard library dependencies.
pub fn resolve_std<'cfg>(
    ws: &Workspace<'cfg>,
    crates: &[String],
) -> CargoResult<(PackageSet<'cfg>, Resolve)> {
    let src_path = detect_sysroot_src_path(ws)?;
    let to_patch = [
        "rustc-std-workspace-core",
        "rustc-std-workspace-alloc",
        "rustc-std-workspace-std",
    ];
    let patches = to_patch
        .iter()
        .map(|name| {
            let source_path = SourceId::for_path(&src_path.join("src").join("tools").join(name))?;
            let dep = Dependency::parse_no_deprecated(name, None, source_path)?;
            Ok(dep)
        })
        .collect::<CargoResult<Vec<_>>>()?;
    let crates_io_url = crate::sources::CRATES_IO_INDEX.parse().unwrap();
    let mut patch = HashMap::new();
    patch.insert(crates_io_url, patches);
    let members = vec![
        String::from("src/libstd"),
        String::from("src/libcore"),
        String::from("src/liballoc"),
        String::from("src/libtest"),
    ];
    let ws_config = crate::core::WorkspaceConfig::Root(crate::core::WorkspaceRootConfig::new(
        &src_path,
        &Some(members),
        /*default_members*/ &None,
        /*exclude*/ &None,
    ));
    let virtual_manifest = crate::core::VirtualManifest::new(
        /*replace*/ Vec::new(),
        patch,
        ws_config,
        // Profiles are not used here, but we need something to pass in.
        ws.profiles().clone(),
        crate::core::Features::default(),
    );

    let config = ws.config();
    // This is a delicate hack. In order for features to resolve correctly,
    // the resolver needs to run a specific "current" member of the workspace.
    // Thus, in order to set the features for `std`, we need to set `libtest`
    // to be the "current" member. `libtest` is the root, and all other
    // standard library crates are dependencies from there. Since none of the
    // other crates need to alter their features, this should be fine, for
    // now. Perhaps in the future features will be decoupled from the resolver
    // and it will be easier to control feature selection.
    let current_manifest = src_path.join("src/libtest/Cargo.toml");
    // TODO: Consider setting require_option_deps false?
    // TODO: Consider doing something to enforce --locked? Or to prevent the
    // lock file from being written, such as setting ephemeral.
    let std_ws = Workspace::new_virtual(src_path, current_manifest, virtual_manifest, config)?;
    // `test` is not in the default set because it is optional, but it needs
    // to be part of the resolve in case we do need it.
    let mut spec_pkgs = Vec::from(crates);
    spec_pkgs.push("test".to_string());
    let spec = Packages::Packages(spec_pkgs);
    let specs = spec.to_package_id_specs(&std_ws)?;
    let features = vec!["panic-unwind".to_string(), "backtrace".to_string()];
    // dev_deps setting shouldn't really matter here.
    let opts = ResolveOpts::new(
        /*dev_deps*/ false, &features, /*all_features*/ false,
        /*uses_default_features*/ true,
    );
    let resolve = ops::resolve_ws_with_opts(&std_ws, opts, &specs)?;
    Ok(resolve)
}

/// Generate a list of root `Unit`s for the standard library.
///
/// The given slice of crate names is the root set.
pub fn generate_std_roots<'a>(
    bcx: &BuildContext<'a, '_>,
    crates: &[String],
    std_resolve: &'a Resolve,
) -> CargoResult<Vec<Unit<'a>>> {
    // Generate the root Units for the standard library.
    let std_ids = crates
        .iter()
        .map(|crate_name| std_resolve.query(crate_name))
        .collect::<CargoResult<Vec<PackageId>>>()?;
    // Convert PackageId to Package.
    let std_pkgs = bcx.packages.get_many(std_ids)?;
    // Generate a list of Units.
    std_pkgs
        .into_iter()
        .map(|pkg| {
            let lib = pkg
                .targets()
                .iter()
                .find(|t| t.is_lib())
                .expect("std has a lib");
            let unit_for = UnitFor::new_normal();
            // I don't think we need to bother with Check here, the difference
            // in time is minimal, and the difference in caching is
            // significant.
            let mode = CompileMode::Build;
            let profile = bcx.profiles.get_profile(
                pkg.package_id(),
                /*is_member*/ false,
                unit_for,
                mode,
                bcx.build_config.release,
            );
            let features = std_resolve.features_sorted(pkg.package_id());
            Ok(bcx
                .units
                .intern(pkg, lib, profile, Kind::Target, mode, features))
        })
        .collect::<CargoResult<Vec<_>>>()
}

fn detect_sysroot_src_path(ws: &Workspace<'_>) -> CargoResult<PathBuf> {
    // NOTE: This is temporary until we figure out how to acquire the source.
    // If we decide to keep the sysroot probe, then BuildConfig will need to
    // be restructured so that the TargetInfo is created earlier and passed
    // in, so we don't have this extra call to rustc.
    let rustc = ws.config().load_global_rustc(Some(ws))?;
    let output = rustc.process().arg("--print=sysroot").exec_with_output()?;
    let s = String::from_utf8(output.stdout)
        .map_err(|e| failure::format_err!("rustc didn't return utf8 output: {:?}", e))?;
    let sysroot = PathBuf::from(s.trim());
    let src_path = sysroot.join("lib").join("rustlib").join("src").join("rust");
    let lock = src_path.join("Cargo.lock");
    if !lock.exists() {
        failure::bail!(
            "{:?} does not exist, unable to build with the standard \
             library, try:\n        rustup component add rust-src",
            lock
        );
    }
    Ok(src_path)
}
<commit_msg>Fix type error due to signature change.<commit_after>//! Code for building the standard library.

use crate::core::compiler::{BuildContext, CompileMode, Kind, Unit};
use crate::core::profiles::UnitFor;
use crate::core::resolver::ResolveOpts;
use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspace};
use crate::ops::{self, Packages};
use crate::util::errors::CargoResult;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

/// Parse the `-Zbuild-std` flag.
pub fn parse_unstable_flag(value: Option<&str>) -> Vec<String> {
    // This is a temporary hack until there is a more principled way to
    // declare dependencies in Cargo.toml.
    let value = value.unwrap_or("std");
    let mut crates: HashSet<&str> = value.split(',').collect();
    if crates.contains("std") {
        crates.insert("core");
        crates.insert("panic_unwind");
        crates.insert("compiler_builtins");
    } else if crates.contains("core") {
        crates.insert("compiler_builtins");
    }
    crates.into_iter().map(|s| s.to_string()).collect()
}

/// Resolve the standard library dependencies.
pub fn resolve_std<'cfg>(
    ws: &Workspace<'cfg>,
    crates: &[String],
) -> CargoResult<(PackageSet<'cfg>, Resolve)> {
    let src_path = detect_sysroot_src_path(ws)?;
    let to_patch = [
        "rustc-std-workspace-core",
        "rustc-std-workspace-alloc",
        "rustc-std-workspace-std",
    ];
    let patches = to_patch
        .iter()
        .map(|&name| {
            let source_path = SourceId::for_path(&src_path.join("src").join("tools").join(name))?;
            let dep = Dependency::parse_no_deprecated(name, None, source_path)?;
            Ok(dep)
        })
        .collect::<CargoResult<Vec<_>>>()?;
    let crates_io_url = crate::sources::CRATES_IO_INDEX.parse().unwrap();
    let mut patch = HashMap::new();
    patch.insert(crates_io_url, patches);
    let members = vec![
        String::from("src/libstd"),
        String::from("src/libcore"),
        String::from("src/liballoc"),
        String::from("src/libtest"),
    ];
    let ws_config = crate::core::WorkspaceConfig::Root(crate::core::WorkspaceRootConfig::new(
        &src_path,
        &Some(members),
        /*default_members*/ &None,
        /*exclude*/ &None,
    ));
    let virtual_manifest = crate::core::VirtualManifest::new(
        /*replace*/ Vec::new(),
        patch,
        ws_config,
        // Profiles are not used here, but we need something to pass in.
        ws.profiles().clone(),
        crate::core::Features::default(),
    );

    let config = ws.config();
    // This is a delicate hack. In order for features to resolve correctly,
    // the resolver needs to run a specific "current" member of the workspace.
    // Thus, in order to set the features for `std`, we need to set `libtest`
    // to be the "current" member. `libtest` is the root, and all other
    // standard library crates are dependencies from there. Since none of the
    // other crates need to alter their features, this should be fine, for
    // now. Perhaps in the future features will be decoupled from the resolver
    // and it will be easier to control feature selection.
    let current_manifest = src_path.join("src/libtest/Cargo.toml");
    // TODO: Consider setting require_option_deps false?
    // TODO: Consider doing something to enforce --locked? Or to prevent the
    // lock file from being written, such as setting ephemeral.
    let std_ws = Workspace::new_virtual(src_path, current_manifest, virtual_manifest, config)?;
    // `test` is not in the default set because it is optional, but it needs
    // to be part of the resolve in case we do need it.
    let mut spec_pkgs = Vec::from(crates);
    spec_pkgs.push("test".to_string());
    let spec = Packages::Packages(spec_pkgs);
    let specs = spec.to_package_id_specs(&std_ws)?;
    let features = vec!["panic-unwind".to_string(), "backtrace".to_string()];
    // dev_deps setting shouldn't really matter here.
    let opts = ResolveOpts::new(
        /*dev_deps*/ false, &features, /*all_features*/ false,
        /*uses_default_features*/ true,
    );
    let resolve = ops::resolve_ws_with_opts(&std_ws, opts, &specs)?;
    Ok(resolve)
}

/// Generate a list of root `Unit`s for the standard library.
///
/// The given slice of crate names is the root set.
pub fn generate_std_roots<'a>(
    bcx: &BuildContext<'a, '_>,
    crates: &[String],
    std_resolve: &'a Resolve,
) -> CargoResult<Vec<Unit<'a>>> {
    // Generate the root Units for the standard library.
    let std_ids = crates
        .iter()
        .map(|crate_name| std_resolve.query(crate_name))
        .collect::<CargoResult<Vec<PackageId>>>()?;
    // Convert PackageId to Package.
    let std_pkgs = bcx.packages.get_many(std_ids)?;
    // Generate a list of Units.
    std_pkgs
        .into_iter()
        .map(|pkg| {
            let lib = pkg
                .targets()
                .iter()
                .find(|t| t.is_lib())
                .expect("std has a lib");
            let unit_for = UnitFor::new_normal();
            // I don't think we need to bother with Check here, the difference
            // in time is minimal, and the difference in caching is
            // significant.
            let mode = CompileMode::Build;
            let profile = bcx.profiles.get_profile(
                pkg.package_id(),
                /*is_member*/ false,
                unit_for,
                mode,
                bcx.build_config.release,
            );
            let features = std_resolve.features_sorted(pkg.package_id());
            Ok(bcx
                .units
                .intern(pkg, lib, profile, Kind::Target, mode, features))
        })
        .collect::<CargoResult<Vec<_>>>()
}

fn detect_sysroot_src_path(ws: &Workspace<'_>) -> CargoResult<PathBuf> {
    // NOTE: This is temporary until we figure out how to acquire the source.
    // If we decide to keep the sysroot probe, then BuildConfig will need to
    // be restructured so that the TargetInfo is created earlier and passed
    // in, so we don't have this extra call to rustc.
    let rustc = ws.config().load_global_rustc(Some(ws))?;
    let output = rustc.process().arg("--print=sysroot").exec_with_output()?;
    let s = String::from_utf8(output.stdout)
        .map_err(|e| failure::format_err!("rustc didn't return utf8 output: {:?}", e))?;
    let sysroot = PathBuf::from(s.trim());
    let src_path = sysroot.join("lib").join("rustlib").join("src").join("rust");
    let lock = src_path.join("Cargo.lock");
    if !lock.exists() {
        failure::bail!(
            "{:?} does not exist, unable to build with the standard \
             library, try:\n        rustup component add rust-src",
            lock
        );
    }
    Ok(src_path)
}
<|endoftext|>


<commit_before>#![cfg(not(syn_disable_nightly_tests))]
#![recursion_limit = "1024"]
#![feature(rustc_private)]

//! The tests in this module do the following:
//!
//! 1. Parse a given expression in both `syn` and `libsyntax`.
//! 2. Fold over the expression adding brackets around each subexpression (with
//!    some complications - see the `syn_brackets` and `libsyntax_brackets`
//!    methods).
//! 3. Serialize the `syn` expression back into a string, and re-parse it with
//!    `libsyntax`.
//! 4. Respan all of the expressions, replacing the spans with the default
//!    spans.
//! 5. Compare the expressions with one another, if they are not equal fail.

extern crate rustc_data_structures;
extern crate rustc_span;
extern crate syntax;

mod features;

use quote::quote;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use regex::Regex;
use rustc_span::edition::Edition;
use syntax::ast;
use syntax::ptr::P;
use walkdir::{DirEntry, WalkDir};

use std::fs::File;
use std::io::Read;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};

use common::eq::SpanlessEq;
use common::parse;

#[macro_use]
mod macros;

#[allow(dead_code)]
mod common;

mod repo;

/// Test some pre-set expressions chosen by us.
#[test]
fn test_simple_precedence() {
    const EXPRS: &[&str] = &[
        "1 + 2 * 3 + 4",
        "1 + 2 * ( 3 + 4 )",
        "{ for i in r { } *some_ptr += 1; }",
        "{ loop { break 5; } }",
        "{ if true { () }.mthd() }",
        "{ for i in unsafe { 20 } { } }",
    ];

    let mut failed = 0;

    for input in EXPRS {
        let expr = if let Some(expr) = parse::syn_expr(input) {
            expr
        } else {
            failed += 1;
            continue;
        };

        let pf = match test_expressions(vec![expr]) {
            (1, 0) => "passed",
            (0, 1) => {
                failed += 1;
                "failed"
            }
            _ => unreachable!(),
        };
        errorf!("=== {}: {}\n", input, pf);
    }

    if failed > 0 {
        panic!("Failed {} tests", failed);
    }
}

/// Test expressions from rustc, like in `test_round_trip`.
#[test]
fn test_rustc_precedence() {
    repo::clone_rust();
    let abort_after = common::abort_after();
    if abort_after == 0 {
        panic!("Skipping all precedence tests");
    }

    let passed = AtomicUsize::new(0);
    let failed = AtomicUsize::new(0);

    // 2018 edition is hard
    let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();

    WalkDir::new("tests/rust")
        .sort_by(|a, b| a.file_name().cmp(b.file_name()))
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
        .unwrap()
        .into_par_iter()
        .for_each(|entry| {
            let path = entry.path();
            if path.is_dir() {
                return;
            }

            // Our version of `libsyntax` can't parse this tests
            if path
                .to_str()
                .unwrap()
                .ends_with("optional_comma_in_match_arm.rs")
            {
                return;
            }

            let mut file = File::open(path).unwrap();
            let mut content = String::new();
            file.read_to_string(&mut content).unwrap();
            let content = edition_regex.replace_all(&content, "_$0");

            let (l_passed, l_failed) = match syn::parse_file(&content) {
                Ok(file) => {
                    let exprs = collect_exprs(file);
                    test_expressions(exprs)
                }
                Err(msg) => {
                    errorf!("syn failed to parse\n{:?}\n", msg);
                    (0, 1)
                }
            };

            errorf!(
                "=== {}: {} passed | {} failed\n",
                path.display(),
                l_passed,
                l_failed
            );

            passed.fetch_add(l_passed, Ordering::SeqCst);
            let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);

            if prev_failed + l_failed >= abort_after {
                process::exit(1);
            }
        });

    let passed = passed.load(Ordering::SeqCst);
    let failed = failed.load(Ordering::SeqCst);

    errorf!("\n===== Precedence Test Results =====\n");
    errorf!("{} passed | {} failed\n", passed, failed);

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
    let mut passed = 0;
    let mut failed = 0;

    syntax::with_globals(Edition::Edition2018, || {
        for expr in exprs {
            let raw = quote!(#expr).to_string();

            let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - libsyntax failed to parse raw\n");
                continue;
            };

            let syn_expr = syn_brackets(expr);
            let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
                continue;
            };

            if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
                passed += 1;
            } else {
                failed += 1;
                errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
            }
        }
    });

    (passed, failed)
}

fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
    parse::libsyntax_expr(input).and_then(libsyntax_brackets)
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precidence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
///
/// This method operates on libsyntax objects.
fn libsyntax_brackets(mut libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
    use rustc_data_structures::thin_vec::ThinVec;
    use rustc_span::DUMMY_SP;
    use std::mem;
    use syntax::ast::{Block, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
    use syntax::mut_visit::MutVisitor;
    use syntax::util::map_in_place::MapInPlace;

    struct BracketsVisitor {
        failed: bool,
    };

    fn flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> Vec<Field> {
        if f.is_shorthand {
            noop_visit_expr(&mut f.expr, vis);
        } else {
            vis.visit_expr(&mut f.expr);
        }
        vec![f]
    }

    fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
        let kind = match stmt.kind {
            // Don't wrap toplevel expressions in statements.
            StmtKind::Expr(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Expr(e)
            }
            StmtKind::Semi(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Semi(e)
            }
            s => s,
        };

        vec![Stmt { kind, ..stmt }]
    }

    fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
        use syntax::mut_visit::{noop_visit_expr, visit_opt, visit_thin_attrs};
        match &mut e.kind {
            ExprKind::Struct(path, fields, expr) => {
                vis.visit_path(path);
                fields.flat_map_in_place(|field| flat_map_field(field, vis));
                visit_opt(expr, |expr| vis.visit_expr(expr));
                vis.visit_id(&mut e.id);
                vis.visit_span(&mut e.span);
                visit_thin_attrs(&mut e.attrs, vis);
            }
            _ => noop_visit_expr(e, vis),
        }
    }

    impl MutVisitor for BracketsVisitor {
        fn visit_expr(&mut self, e: &mut P<Expr>) {
            noop_visit_expr(e, self);
            match e.kind {
                ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
                _ => {
                    let inner = mem::replace(
                        e,
                        P(Expr {
                            id: ast::DUMMY_NODE_ID,
                            kind: ExprKind::Err,
                            span: DUMMY_SP,
                            attrs: ThinVec::new(),
                        }),
                    );
                    e.kind = ExprKind::Paren(inner);
                }
            }
        }

        fn visit_block(&mut self, block: &mut P<Block>) {
            self.visit_id(&mut block.id);
            block
                .stmts
                .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
            self.visit_span(&mut block.span);
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn visit_pat(&mut self, pat: &mut P<Pat>) {
            let _ = pat;
        }

        fn visit_ty(&mut self, ty: &mut P<Ty>) {
            let _ = ty;
        }

        fn visit_mac(&mut self, mac: &mut Mac) {
            // By default when folding over macros, libsyntax panics. This is
            // because it's usually not what you want, you want to run after
            // macro expansion. We do want to do that (syn doesn't do macro
            // expansion), so we implement visit_mac to just return the macro
            // unchanged.
            let _ = mac;
        }
    }

    let mut folder = BracketsVisitor { failed: false };
    folder.visit_expr(&mut libsyntax_expr);
    if folder.failed {
        None
    } else {
        Some(libsyntax_expr)
    }
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precedence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
    use syn::fold::*;
    use syn::*;

    struct ParenthesizeEveryExpr;
    impl Fold for ParenthesizeEveryExpr {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            match expr {
                Expr::Group(_) => unreachable!(),
                Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
                    fold_expr(self, expr)
                }
                _ => Expr::Paren(ExprParen {
                    attrs: Vec::new(),
                    expr: Box::new(fold_expr(self, expr)),
                    paren_token: token::Paren::default(),
                }),
            }
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
            match stmt {
                // Don't wrap toplevel expressions in statements.
                Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
                Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
                s => s,
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: Pat) -> Pat {
            pat
        }

        fn fold_type(&mut self, ty: Type) -> Type {
            ty
        }
    }

    let mut folder = ParenthesizeEveryExpr;
    folder.fold_expr(syn_expr)
}

/// Walk through a crate collecting all expressions we can find in it.
fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
    use syn::fold::*;
    use syn::punctuated::Punctuated;
    use syn::*;

    struct CollectExprs(Vec<Expr>);
    impl Fold for CollectExprs {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            self.0.push(expr);

            Expr::Tuple(ExprTuple {
                attrs: vec![],
                elems: Punctuated::new(),
                paren_token: token::Paren::default(),
            })
        }
    }

    let mut folder = CollectExprs(vec![]);
    folder.fold_file(file);
    folder.0
}
<commit_msg>Omit empty exprs from precedence test<commit_after>#![cfg(not(syn_disable_nightly_tests))]
#![recursion_limit = "1024"]
#![feature(rustc_private)]

//! The tests in this module do the following:
//!
//! 1. Parse a given expression in both `syn` and `libsyntax`.
//! 2. Fold over the expression adding brackets around each subexpression (with
//!    some complications - see the `syn_brackets` and `libsyntax_brackets`
//!    methods).
//! 3. Serialize the `syn` expression back into a string, and re-parse it with
//!    `libsyntax`.
//! 4. Respan all of the expressions, replacing the spans with the default
//!    spans.
//! 5. Compare the expressions with one another, if they are not equal fail.

extern crate rustc_data_structures;
extern crate rustc_span;
extern crate syntax;

mod features;

use quote::quote;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use regex::Regex;
use rustc_span::edition::Edition;
use syntax::ast;
use syntax::ptr::P;
use walkdir::{DirEntry, WalkDir};

use std::fs::File;
use std::io::Read;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};

use common::eq::SpanlessEq;
use common::parse;

#[macro_use]
mod macros;

#[allow(dead_code)]
mod common;

mod repo;

/// Test some pre-set expressions chosen by us.
#[test]
fn test_simple_precedence() {
    const EXPRS: &[&str] = &[
        "1 + 2 * 3 + 4",
        "1 + 2 * ( 3 + 4 )",
        "{ for i in r { } *some_ptr += 1; }",
        "{ loop { break 5; } }",
        "{ if true { () }.mthd() }",
        "{ for i in unsafe { 20 } { } }",
    ];

    let mut failed = 0;

    for input in EXPRS {
        let expr = if let Some(expr) = parse::syn_expr(input) {
            expr
        } else {
            failed += 1;
            continue;
        };

        let pf = match test_expressions(vec![expr]) {
            (1, 0) => "passed",
            (0, 1) => {
                failed += 1;
                "failed"
            }
            _ => unreachable!(),
        };
        errorf!("=== {}: {}\n", input, pf);
    }

    if failed > 0 {
        panic!("Failed {} tests", failed);
    }
}

/// Test expressions from rustc, like in `test_round_trip`.
#[test]
fn test_rustc_precedence() {
    repo::clone_rust();
    let abort_after = common::abort_after();
    if abort_after == 0 {
        panic!("Skipping all precedence tests");
    }

    let passed = AtomicUsize::new(0);
    let failed = AtomicUsize::new(0);

    // 2018 edition is hard
    let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();

    WalkDir::new("tests/rust")
        .sort_by(|a, b| a.file_name().cmp(b.file_name()))
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
        .unwrap()
        .into_par_iter()
        .for_each(|entry| {
            let path = entry.path();
            if path.is_dir() {
                return;
            }

            // Our version of `libsyntax` can't parse this tests
            if path
                .to_str()
                .unwrap()
                .ends_with("optional_comma_in_match_arm.rs")
            {
                return;
            }

            let mut file = File::open(path).unwrap();
            let mut content = String::new();
            file.read_to_string(&mut content).unwrap();
            let content = edition_regex.replace_all(&content, "_$0");

            let (l_passed, l_failed) = match syn::parse_file(&content) {
                Ok(file) => {
                    let exprs = collect_exprs(file);
                    test_expressions(exprs)
                }
                Err(msg) => {
                    errorf!("syn failed to parse\n{:?}\n", msg);
                    (0, 1)
                }
            };

            errorf!(
                "=== {}: {} passed | {} failed\n",
                path.display(),
                l_passed,
                l_failed
            );

            passed.fetch_add(l_passed, Ordering::SeqCst);
            let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);

            if prev_failed + l_failed >= abort_after {
                process::exit(1);
            }
        });

    let passed = passed.load(Ordering::SeqCst);
    let failed = failed.load(Ordering::SeqCst);

    errorf!("\n===== Precedence Test Results =====\n");
    errorf!("{} passed | {} failed\n", passed, failed);

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
    let mut passed = 0;
    let mut failed = 0;

    syntax::with_globals(Edition::Edition2018, || {
        for expr in exprs {
            let raw = quote!(#expr).to_string();

            let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - libsyntax failed to parse raw\n");
                continue;
            };

            let syn_expr = syn_brackets(expr);
            let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
                e
            } else {
                failed += 1;
                errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
                continue;
            };

            if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
                passed += 1;
            } else {
                failed += 1;
                errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
            }
        }
    });

    (passed, failed)
}

fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
    parse::libsyntax_expr(input).and_then(libsyntax_brackets)
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precidence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
///
/// This method operates on libsyntax objects.
fn libsyntax_brackets(mut libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
    use rustc_data_structures::thin_vec::ThinVec;
    use rustc_span::DUMMY_SP;
    use std::mem;
    use syntax::ast::{Block, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
    use syntax::mut_visit::MutVisitor;
    use syntax::util::map_in_place::MapInPlace;

    struct BracketsVisitor {
        failed: bool,
    };

    fn flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> Vec<Field> {
        if f.is_shorthand {
            noop_visit_expr(&mut f.expr, vis);
        } else {
            vis.visit_expr(&mut f.expr);
        }
        vec![f]
    }

    fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
        let kind = match stmt.kind {
            // Don't wrap toplevel expressions in statements.
            StmtKind::Expr(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Expr(e)
            }
            StmtKind::Semi(mut e) => {
                noop_visit_expr(&mut e, vis);
                StmtKind::Semi(e)
            }
            s => s,
        };

        vec![Stmt { kind, ..stmt }]
    }

    fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
        use syntax::mut_visit::{noop_visit_expr, visit_opt, visit_thin_attrs};
        match &mut e.kind {
            ExprKind::Struct(path, fields, expr) => {
                vis.visit_path(path);
                fields.flat_map_in_place(|field| flat_map_field(field, vis));
                visit_opt(expr, |expr| vis.visit_expr(expr));
                vis.visit_id(&mut e.id);
                vis.visit_span(&mut e.span);
                visit_thin_attrs(&mut e.attrs, vis);
            }
            _ => noop_visit_expr(e, vis),
        }
    }

    impl MutVisitor for BracketsVisitor {
        fn visit_expr(&mut self, e: &mut P<Expr>) {
            noop_visit_expr(e, self);
            match e.kind {
                ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
                _ => {
                    let inner = mem::replace(
                        e,
                        P(Expr {
                            id: ast::DUMMY_NODE_ID,
                            kind: ExprKind::Err,
                            span: DUMMY_SP,
                            attrs: ThinVec::new(),
                        }),
                    );
                    e.kind = ExprKind::Paren(inner);
                }
            }
        }

        fn visit_block(&mut self, block: &mut P<Block>) {
            self.visit_id(&mut block.id);
            block
                .stmts
                .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
            self.visit_span(&mut block.span);
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn visit_pat(&mut self, pat: &mut P<Pat>) {
            let _ = pat;
        }

        fn visit_ty(&mut self, ty: &mut P<Ty>) {
            let _ = ty;
        }

        fn visit_mac(&mut self, mac: &mut Mac) {
            // By default when folding over macros, libsyntax panics. This is
            // because it's usually not what you want, you want to run after
            // macro expansion. We do want to do that (syn doesn't do macro
            // expansion), so we implement visit_mac to just return the macro
            // unchanged.
            let _ = mac;
        }
    }

    let mut folder = BracketsVisitor { failed: false };
    folder.visit_expr(&mut libsyntax_expr);
    if folder.failed {
        None
    } else {
        Some(libsyntax_expr)
    }
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precedence of the parsed expressions, and produce a stringified
/// form of the resulting expression.
fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
    use syn::fold::*;
    use syn::*;

    struct ParenthesizeEveryExpr;
    impl Fold for ParenthesizeEveryExpr {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            match expr {
                Expr::Group(_) => unreachable!(),
                Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
                    fold_expr(self, expr)
                }
                _ => Expr::Paren(ExprParen {
                    attrs: Vec::new(),
                    expr: Box::new(fold_expr(self, expr)),
                    paren_token: token::Paren::default(),
                }),
            }
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
            match stmt {
                // Don't wrap toplevel expressions in statements.
                Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
                Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
                s => s,
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: Pat) -> Pat {
            pat
        }

        fn fold_type(&mut self, ty: Type) -> Type {
            ty
        }
    }

    let mut folder = ParenthesizeEveryExpr;
    folder.fold_expr(syn_expr)
}

/// Walk through a crate collecting all expressions we can find in it.
fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
    use syn::fold::*;
    use syn::punctuated::Punctuated;
    use syn::*;

    struct CollectExprs(Vec<Expr>);
    impl Fold for CollectExprs {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            match expr {
                Expr::Verbatim(tokens) if tokens.is_empty() => {}
                _ => self.0.push(expr),
            }

            Expr::Tuple(ExprTuple {
                attrs: vec![],
                elems: Punctuated::new(),
                paren_token: token::Paren::default(),
            })
        }
    }

    let mut folder = CollectExprs(vec![]);
    folder.fold_file(file);
    folder.0
}
<|endoftext|>


<commit_before>use rustc::hir;
use rustc::lint::*;
use rustc::ty;
use std::borrow::Cow;
use utils::{in_macro, iter_input_pats, match_type, method_chain_args, snippet, span_lint_and_then};
use utils::paths;

#[derive(Clone)]
pub struct Pass;

/// **What it does:** Checks for usage of `Option.map(f)` where f is a nil
/// function or closure
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if statement
///
/// **Known problems:** Closures with multiple statements are not handled
///
/// **Example:**
/// ```rust
/// let x : Option<&str> = do_stuff();
/// x.map(log_err_msg);
/// x.map(|msg| log_err_msg(format_msg(msg)))
/// ```
/// The correct use would be:
/// ```rust
/// let x : Option<&str> = do_stuff();
/// if let Some(msg) = x {
///     log_err_msg(msg)
/// }
/// if let Some(msg) = x {
///     log_err_msg(format_msg(msg))
/// }
/// ```
declare_lint! {
    pub OPTION_MAP_NIL_FN,
    Allow,
    "using `Option.map(f)`, where f is a nil function or closure"
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_MAP_NIL_FN)
    }
}

fn is_nil_type(ty: ty::Ty) -> bool {
    match ty.sty {
        ty::TyTuple(slice) => slice.is_empty(),
        ty::TyNever => true,
        _ => false,
    }
}

fn is_nil_function(cx: &LateContext, expr: &hir::Expr) -> bool {
    let ty = cx.tables.expr_ty(expr);

    if let ty::TyFnDef(_, _, bare) = ty.sty {
        if let Some(fn_type) = cx.tcx.no_late_bound_regions(&bare.sig) {
            return is_nil_type(fn_type.output());
        }
    }
    false
}

fn is_nil_expression(cx: &LateContext, expr: &hir::Expr) -> bool {
    is_nil_type(cx.tables.expr_ty(expr))
}

// The expression inside a closure may or may not have surrounding braces and
// semicolons, which causes problems when generating a suggestion. Given an
// expression that evaluates to '()' or '!', recursively remove useless braces
// and semi-colons until is suitable for including in the suggestion template
fn reduce_nil_expression<'a>(cx: &LateContext, expr: &'a hir::Expr) -> Option<Cow<'a, str>> {
    if !is_nil_expression(cx, expr) {
        return None;
    }

    match expr.node {
        hir::ExprCall(_, _) |
        hir::ExprMethodCall(_, _, _) => {
            // Calls can't be reduced any more
            Some(snippet(cx, expr.span, "_"))
        },
        hir::ExprBlock(ref block) => {
            match (&block.stmts[..], block.expr.as_ref()) {
                (&[], Some(inner_expr)) => {
                    // Reduce `{ X }` to `X`
                    reduce_nil_expression(cx, inner_expr)
                },
                (&[ref inner_stmt], None) => {
                    // Reduce `{ X; }` to `X` or `X;`
                    match inner_stmt.node {
                        hir::StmtDecl(ref d, _) => Some(snippet(cx, d.span, "_")),
                        hir::StmtExpr(ref e, _) => Some(snippet(cx, e.span, "_")),
                        hir::StmtSemi(ref e, _) => {
                            if is_nil_expression(cx, e) {
                                // `X` returns nil so we can strip the
                                // semicolon and reduce further
                                reduce_nil_expression(cx, e)
                            } else {
                                // `X` doesn't return nil so it needs a
                                // trailing semicolon
                                Some(snippet(cx, inner_stmt.span, "_"))
                            }
                        },
                    }
                },
                _ => None,
            }
        },
        _ => None,
    }
}

fn reduce_nil_closure<'a, 'tcx>(
    cx: &LateContext<'a, 'tcx>,
    expr: &'a hir::Expr
) -> Option<(Cow<'a, str>, Cow<'a, str>)> {
    if let hir::ExprClosure(_, ref decl, inner_expr_id, _) = expr.node {
        let body = cx.tcx.map.body(inner_expr_id);

        if_let_chain! {[
            decl.inputs.len() == 1,
            let Some(binding) = iter_input_pats(&decl, body).next(),
            let Some(expr_snippet) = reduce_nil_expression(cx, &body.value),
        ], {
            let binding_snippet = snippet(cx, binding.pat.span, "_");
            return Some((binding_snippet, expr_snippet));
        }}
    }
    None
}

fn lint_map_nil_fn(cx: &LateContext, stmt: &hir::Stmt, expr: &hir::Expr, map_args: &[hir::Expr]) {
    let var_arg = &map_args[0];
    let fn_arg = &map_args[1];

    if !match_type(cx, cx.tables.expr_ty(var_arg), &paths::OPTION) {
        return;
    }

    if is_nil_function(cx, fn_arg) {
        let msg = "called `map(f)` on an Option value where `f` is a nil function";
        let suggestion = format!("if let Some(...) = {0} {{ {1}(...) }}",
                                 snippet(cx, var_arg.span, "_"),
                                 snippet(cx, fn_arg.span, "_"));

        span_lint_and_then(cx,
                           OPTION_MAP_NIL_FN,
                           expr.span,
                           msg,
                           |db| { db.span_suggestion(stmt.span, "try this", suggestion); });
    } else if let Some((binding_snippet, expr_snippet)) = reduce_nil_closure(cx, fn_arg) {
        let msg = "called `map(f)` on an Option value where `f` is a nil closure";
        let suggestion = format!("if let Some({0}) = {1} {{ {2} }}",
                                 binding_snippet,
                                 snippet(cx, var_arg.span, "_"),
                                 expr_snippet);

        span_lint_and_then(cx,
                           OPTION_MAP_NIL_FN,
                           expr.span,
                           msg,
                           |db| { db.span_suggestion(stmt.span, "try this", suggestion); });
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_stmt(&mut self, cx: &LateContext, stmt: &hir::Stmt) {
        if in_macro(cx, stmt.span) {
            return;
        }

        if let hir::StmtSemi(ref expr, _) = stmt.node {
            if let hir::ExprMethodCall(_, _, _) = expr.node {
                if let Some(arglists) = method_chain_args(expr, &["map"]) {
                    lint_map_nil_fn(cx, stmt, expr, arglists[0]);
                }
            }
        }
    }
}
<commit_msg>Return Spans instead of Cow<&str>'s<commit_after>use rustc::hir;
use rustc::lint::*;
use rustc::ty;
use syntax::codemap::Span;
use utils::{in_macro, iter_input_pats, match_type, method_chain_args, snippet, span_lint_and_then};
use utils::paths;

#[derive(Clone)]
pub struct Pass;

/// **What it does:** Checks for usage of `Option.map(f)` where f is a nil
/// function or closure
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if statement
///
/// **Known problems:** Closures with multiple statements are not handled
///
/// **Example:**
/// ```rust
/// let x : Option<&str> = do_stuff();
/// x.map(log_err_msg);
/// x.map(|msg| log_err_msg(format_msg(msg)))
/// ```
/// The correct use would be:
/// ```rust
/// let x : Option<&str> = do_stuff();
/// if let Some(msg) = x {
///     log_err_msg(msg)
/// }
/// if let Some(msg) = x {
///     log_err_msg(format_msg(msg))
/// }
/// ```
declare_lint! {
    pub OPTION_MAP_NIL_FN,
    Allow,
    "using `Option.map(f)`, where f is a nil function or closure"
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_MAP_NIL_FN)
    }
}

fn is_nil_type(ty: ty::Ty) -> bool {
    match ty.sty {
        ty::TyTuple(slice) => slice.is_empty(),
        ty::TyNever => true,
        _ => false,
    }
}

fn is_nil_function(cx: &LateContext, expr: &hir::Expr) -> bool {
    let ty = cx.tables.expr_ty(expr);

    if let ty::TyFnDef(_, _, bare) = ty.sty {
        if let Some(fn_type) = cx.tcx.no_late_bound_regions(&bare.sig) {
            return is_nil_type(fn_type.output());
        }
    }
    false
}

fn is_nil_expression(cx: &LateContext, expr: &hir::Expr) -> bool {
    is_nil_type(cx.tables.expr_ty(expr))
}

// The expression inside a closure may or may not have surrounding braces and
// semicolons, which causes problems when generating a suggestion. Given an
// expression that evaluates to '()' or '!', recursively remove useless braces
// and semi-colons until is suitable for including in the suggestion template
fn reduce_nil_expression<'a>(cx: &LateContext, expr: &'a hir::Expr) -> Option<Span> {
    if !is_nil_expression(cx, expr) {
        return None;
    }

    match expr.node {
        hir::ExprCall(_, _) |
        hir::ExprMethodCall(_, _, _) => {
            // Calls can't be reduced any more
            Some(expr.span)
        },
        hir::ExprBlock(ref block) => {
            match (&block.stmts[..], block.expr.as_ref()) {
                (&[], Some(inner_expr)) => {
                    // Reduce `{ X }` to `X`
                    reduce_nil_expression(cx, inner_expr)
                },
                (&[ref inner_stmt], None) => {
                    // Reduce `{ X; }` to `X` or `X;`
                    match inner_stmt.node {
                        hir::StmtDecl(ref d, _) => Some(d.span),
                        hir::StmtExpr(ref e, _) => Some(e.span),
                        hir::StmtSemi(ref e, _) => {
                            if is_nil_expression(cx, e) {
                                // `X` returns nil so we can strip the
                                // semicolon and reduce further
                                reduce_nil_expression(cx, e)
                            } else {
                                // `X` doesn't return nil so it needs a
                                // trailing semicolon
                                Some(inner_stmt.span)
                            }
                        },
                    }
                },
                _ => None,
            }
        },
        _ => None,
    }
}

fn reduce_nil_closure<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'a hir::Expr) -> Option<(Span, Span)> {
    if let hir::ExprClosure(_, ref decl, inner_expr_id, _) = expr.node {
        let body = cx.tcx.map.body(inner_expr_id);

        if_let_chain! {[
            decl.inputs.len() == 1,
            let Some(binding) = iter_input_pats(&decl, body).next(),
            let Some(expr_span) = reduce_nil_expression(cx, &body.value),
        ], {
            return Some((binding.pat.span, expr_span))
        }}
    }
    None
}

fn lint_map_nil_fn(cx: &LateContext, stmt: &hir::Stmt, expr: &hir::Expr, map_args: &[hir::Expr]) {
    let var_arg = &map_args[0];
    let fn_arg = &map_args[1];

    if !match_type(cx, cx.tables.expr_ty(var_arg), &paths::OPTION) {
        return;
    }

    if is_nil_function(cx, fn_arg) {
        let msg = "called `map(f)` on an Option value where `f` is a nil function";
        let suggestion = format!("if let Some(...) = {0} {{ {1}(...) }}",
                                 snippet(cx, var_arg.span, "_"),
                                 snippet(cx, fn_arg.span, "_"));

        span_lint_and_then(cx,
                           OPTION_MAP_NIL_FN,
                           expr.span,
                           msg,
                           |db| { db.span_suggestion(stmt.span, "try this", suggestion); });
    } else if let Some((binding_span, expr_span)) = reduce_nil_closure(cx, fn_arg) {
        let msg = "called `map(f)` on an Option value where `f` is a nil closure";
        let suggestion = format!("if let Some({0}) = {1} {{ {2} }}",
                                 snippet(cx, binding_span, "_"),
                                 snippet(cx, var_arg.span, "_"),
                                 snippet(cx, expr_span, "_"));

        span_lint_and_then(cx,
                           OPTION_MAP_NIL_FN,
                           expr.span,
                           msg,
                           |db| { db.span_suggestion(stmt.span, "try this", suggestion); });
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_stmt(&mut self, cx: &LateContext, stmt: &hir::Stmt) {
        if in_macro(cx, stmt.span) {
            return;
        }

        if let hir::StmtSemi(ref expr, _) = stmt.node {
            if let hir::ExprMethodCall(_, _, _) = expr.node {
                if let Some(arglists) = method_chain_args(expr, &["map"]) {
                    lint_map_nil_fn(cx, stmt, expr, arglists[0]);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use std::collections::HashMap;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::visit::FnKind;
use utils::{span_lint, span_help_and_lint, snippet, span_lint_and_then};
/// **What it does:** This lint checks for structure field patterns bound to wildcards.
///
/// **Why is this bad?** Using `..` instead is shorter and leaves the focus on the fields that are actually bound.
///
/// **Known problems:** None.
///
/// **Example:** `let { a: _, b: ref b, c: _ } = ..`
declare_lint! {
    pub UNNEEDED_FIELD_PATTERN, Warn,
    "Struct fields are bound to a wildcard instead of using `..`"
}

/// **What it does:** This lint checks for function arguments having the similar names differing by an underscore
///
/// **Why is this bad?** It affects code readability
///
/// **Known problems:** None.
///
/// **Example:** `fn foo(a: i32, _a: i32) {}`
declare_lint! {
    pub DUPLICATE_UNDERSCORE_ARGUMENT, Warn,
    "Function arguments having names which only differ by an underscore"
}

/// **What it does:** This lint detects closures called in the same expression where they are defined.
///
/// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
///
/// **Known problems:** None.
///
/// **Example:** `(|| 42)()`
declare_lint! {
    pub REDUNDANT_CLOSURE_CALL, Warn,
    "Closures should not be called in the expression they are defined"
}

/// **What it does:** This lint detects expressions of the form `--x`
///
/// **Why is this bad?** It can mislead C/C++ programmers to think `x` was decremented.
///
/// **Known problems:** None.
///
/// **Example:** `--x;`
declare_lint! {
    pub DOUBLE_NEG, Warn,
    "`--x` is a double negation of `x` and not a pre-decrement as in C or C++"
}

#[derive(Copy, Clone)]
pub struct MiscEarly;

impl LintPass for MiscEarly {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL, DOUBLE_NEG)
    }
}

impl EarlyLintPass for MiscEarly {
    fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
        if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
            let mut wilds = 0;
            let type_name = npat.segments.last().expect("A path must have at least one segment").identifier.name;

            for field in pfields {
                if field.node.pat.node == PatKind::Wild {
                    wilds += 1;
                }
            }
            if !pfields.is_empty() && wilds == pfields.len() {
                span_help_and_lint(cx,
                                   UNNEEDED_FIELD_PATTERN,
                                   pat.span,
                                   "All the struct fields are matched to a wildcard pattern, consider using `..`.",
                                   &format!("Try with `{} {{ .. }}` instead", type_name));
                return;
            }
            if wilds > 0 {
                let mut normal = vec![];

                for field in pfields {
                    if field.node.pat.node != PatKind::Wild {
                        if let Ok(n) = cx.sess().codemap().span_to_snippet(field.span) {
                            normal.push(n);
                        }
                    }
                }
                for field in pfields {
                    if field.node.pat.node == PatKind::Wild {
                        wilds -= 1;
                        if wilds > 0 {
                            span_lint(cx,
                                      UNNEEDED_FIELD_PATTERN,
                                      field.span,
                                      "You matched a field with a wildcard pattern. Consider using `..` instead");
                        } else {
                            span_help_and_lint(cx,
                                               UNNEEDED_FIELD_PATTERN,
                                               field.span,
                                               "You matched a field with a wildcard pattern. Consider using `..` \
                                                instead",
                                               &format!("Try with `{} {{ {}, .. }}`",
                                                        type_name,
                                                        normal[..].join(", ")));
                        }
                    }
                }
            }
        }
    }

    fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
        let mut registered_names: HashMap<String, Span> = HashMap::new();

        for ref arg in &decl.inputs {
            if let PatKind::Ident(_, sp_ident, None) = arg.pat.node {
                let arg_name = sp_ident.node.to_string();

                if arg_name.starts_with('_') {
                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
                        span_lint(cx,
                                  DUPLICATE_UNDERSCORE_ARGUMENT,
                                  *correspondence,
                                  &format!("`{}` already exists, having another argument having almost the same \
                                            name makes code comprehension and documentation more difficult",
                                           arg_name[1..].to_owned()));;
                    }
                } else {
                    registered_names.insert(arg_name, arg.pat.span);
                }
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        match expr.node {
            ExprKind::Call(ref paren, _) => {
                if let ExprKind::Paren(ref closure) = paren.node {
                    if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
                        span_lint_and_then(cx,
                                           REDUNDANT_CLOSURE_CALL,
                                           expr.span,
                                           "Try not to call a closure in the expression where it is declared.",
                                           |db| {
                                               if decl.inputs.is_empty() {
                                                   let hint = format!("{}", snippet(cx, block.span, ".."));
                                                   db.span_suggestion(expr.span, "Try doing something like: ", hint);
                                               }
                                           });
                    }
                }
            }
            ExprKind::Unary(UnOp::Neg, ref inner) => {
                if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
                    span_lint(cx,
                              DOUBLE_NEG,
                              expr.span,
                              "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
    }
            }
            _ => ()
        }
    }

    fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
        for w in block.stmts.windows(2) {
            if_let_chain! {[
                let StmtKind::Local(ref local) = w[0].node,
                let Option::Some(ref t) = local.init,
                let ExprKind::Closure(_, _, _, _) = t.node,
                let PatKind::Ident(_, sp_ident, _) = local.pat.node,
                let StmtKind::Semi(ref second) = w[1].node,
                let ExprKind::Assign(_, ref call) = second.node,
                let ExprKind::Call(ref closure, _) = call.node,
                let ExprKind::Path(_, ref path) = closure.node
            ], {
                if sp_ident.node == (&path.segments[0]).identifier {
                    span_lint(cx, REDUNDANT_CLOSURE_CALL, second.span, "Closure called just once immediately after it was declared");
                }
            }}
        }
    }
}
<commit_msg>Cleanup<commit_after>use rustc::lint::*;
use std::collections::HashMap;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::visit::FnKind;
use utils::{span_lint, span_help_and_lint, snippet, span_lint_and_then};
/// **What it does:** This lint checks for structure field patterns bound to wildcards.
///
/// **Why is this bad?** Using `..` instead is shorter and leaves the focus on the fields that are actually bound.
///
/// **Known problems:** None.
///
/// **Example:** `let { a: _, b: ref b, c: _ } = ..`
declare_lint! {
    pub UNNEEDED_FIELD_PATTERN, Warn,
    "Struct fields are bound to a wildcard instead of using `..`"
}

/// **What it does:** This lint checks for function arguments having the similar names differing by an underscore
///
/// **Why is this bad?** It affects code readability
///
/// **Known problems:** None.
///
/// **Example:** `fn foo(a: i32, _a: i32) {}`
declare_lint! {
    pub DUPLICATE_UNDERSCORE_ARGUMENT, Warn,
    "Function arguments having names which only differ by an underscore"
}

/// **What it does:** This lint detects closures called in the same expression where they are defined.
///
/// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
///
/// **Known problems:** None.
///
/// **Example:** `(|| 42)()`
declare_lint! {
    pub REDUNDANT_CLOSURE_CALL, Warn,
    "Closures should not be called in the expression they are defined"
}

/// **What it does:** This lint detects expressions of the form `--x`
///
/// **Why is this bad?** It can mislead C/C++ programmers to think `x` was decremented.
///
/// **Known problems:** None.
///
/// **Example:** `--x;`
declare_lint! {
    pub DOUBLE_NEG, Warn,
    "`--x` is a double negation of `x` and not a pre-decrement as in C or C++"
}

#[derive(Copy, Clone)]
pub struct MiscEarly;

impl LintPass for MiscEarly {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL, DOUBLE_NEG)
    }
}

impl EarlyLintPass for MiscEarly {
    fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
        if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
            let mut wilds = 0;
            let type_name = npat.segments.last().expect("A path must have at least one segment").identifier.name;

            for field in pfields {
                if field.node.pat.node == PatKind::Wild {
                    wilds += 1;
                }
            }
            if !pfields.is_empty() && wilds == pfields.len() {
                span_help_and_lint(cx,
                                   UNNEEDED_FIELD_PATTERN,
                                   pat.span,
                                   "All the struct fields are matched to a wildcard pattern, consider using `..`.",
                                   &format!("Try with `{} {{ .. }}` instead", type_name));
                return;
            }
            if wilds > 0 {
                let mut normal = vec![];

                for field in pfields {
                    if field.node.pat.node != PatKind::Wild {
                        if let Ok(n) = cx.sess().codemap().span_to_snippet(field.span) {
                            normal.push(n);
                        }
                    }
                }
                for field in pfields {
                    if field.node.pat.node == PatKind::Wild {
                        wilds -= 1;
                        if wilds > 0 {
                            span_lint(cx,
                                      UNNEEDED_FIELD_PATTERN,
                                      field.span,
                                      "You matched a field with a wildcard pattern. Consider using `..` instead");
                        } else {
                            span_help_and_lint(cx,
                                               UNNEEDED_FIELD_PATTERN,
                                               field.span,
                                               "You matched a field with a wildcard pattern. Consider using `..` \
                                                instead",
                                               &format!("Try with `{} {{ {}, .. }}`",
                                                        type_name,
                                                        normal[..].join(", ")));
                        }
                    }
                }
            }
        }
    }

    fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
        let mut registered_names: HashMap<String, Span> = HashMap::new();

        for ref arg in &decl.inputs {
            if let PatKind::Ident(_, sp_ident, None) = arg.pat.node {
                let arg_name = sp_ident.node.to_string();

                if arg_name.starts_with('_') {
                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
                        span_lint(cx,
                                  DUPLICATE_UNDERSCORE_ARGUMENT,
                                  *correspondence,
                                  &format!("`{}` already exists, having another argument having almost the same \
                                            name makes code comprehension and documentation more difficult",
                                           arg_name[1..].to_owned()));;
                    }
                } else {
                    registered_names.insert(arg_name, arg.pat.span);
                }
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        match expr.node {
            ExprKind::Call(ref paren, _) => {
                if let ExprKind::Paren(ref closure) = paren.node {
                    if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
                        span_lint_and_then(cx,
                                           REDUNDANT_CLOSURE_CALL,
                                           expr.span,
                                           "Try not to call a closure in the expression where it is declared.",
                                           |db| {
                                               if decl.inputs.is_empty() {
                                                   let hint = snippet(cx, block.span, "..").into_owned();
                                                   db.span_suggestion(expr.span, "Try doing something like: ", hint);
                                               }
                                           });
                    }
                }
            }
            ExprKind::Unary(UnOp::Neg, ref inner) => {
                if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
                    span_lint(cx,
                              DOUBLE_NEG,
                              expr.span,
                              "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
    }
            }
            _ => ()
        }
    }

    fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
        for w in block.stmts.windows(2) {
            if_let_chain! {[
                let StmtKind::Local(ref local) = w[0].node,
                let Option::Some(ref t) = local.init,
                let ExprKind::Closure(_, _, _, _) = t.node,
                let PatKind::Ident(_, sp_ident, _) = local.pat.node,
                let StmtKind::Semi(ref second) = w[1].node,
                let ExprKind::Assign(_, ref call) = second.node,
                let ExprKind::Call(ref closure, _) = call.node,
                let ExprKind::Path(_, ref path) = closure.node
            ], {
                if sp_ident.node == (&path.segments[0]).identifier {
                    span_lint(cx, REDUNDANT_CLOSURE_CALL, second.span, "Closure called just once immediately after it was declared");
                }
            }}
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Fix column_index when between character boundaries<commit_after><|endoftext|>


<commit_before><commit_msg>Add documentation to Sierpinski triangle example<commit_after><|endoftext|>


<commit_before><commit_msg>Simplify `OscMessage` struct until the decoder is finished<commit_after><|endoftext|>


<commit_before><commit_msg>Rename map to eval_fn.<commit_after><|endoftext|>


<commit_before>use svm::cell::SVMCell;
use svm::slist::List;

/// Trait for AST nodes.
pub trait ASTNode {
    /// Compile this node to a list of SVM expressions
    fn compile(self)                    -> Result<SVMCell, &'static str>;

    /// Pretty-print this node
    fn prettyprint(&self)               -> String { self.print_level(0usize) }

    /// Pretty-print this node at the desired indent level
    fn print_level(&self, level: usize) -> String;
}

/// Expression
///
/// All Seax Scheme expressions are one of the following
///  + Nested S-Expressions
///  + Identifiers
///  + Lists
///  + Numbers
///     - signed int
///     - unsigned int
///     - floating-point
///  + Characters
///  + Strings
///  TODO: implement the entire Scheme 'numeric tower'
///  TODO: macros should happen
#[derive(Clone, PartialEq)]
pub enum ExprNode {
    Root(RootNode),
    SExpr(SExprNode),
    Name(NameNode),
    List(ListNode),
    IntConst(IntNode),
    UIntConst(UIntNode),
    FloatConst(FloatNode),
    BoolConst(BoolNode),
    StringConst(StringNode),
    CharConst(CharNode),
}

/// AST node for the root of a program's AST
#[derive(Clone, PartialEq)]
pub struct RootNode { pub exprs: Vec<ExprNode> }

/// AST node for an S-expression.
///
/// This includes function application, assignment,
/// function definition, et cetera...Scheme is not a complexl anguage.
#[derive(Clone, PartialEq)]
pub struct SExprNode {
    pub operator: NameNode,
    pub operands: Vec<ExprNode>,
}

/// AST node for a list literal
#[derive(Clone, PartialEq)]
pub struct ListNode { pub elements: Vec<ExprNode> }

/// AST node for an identifier
#[derive(Clone, PartialEq)]
pub struct NameNode { pub name: String }

/// AST node for an integer constant
#[derive(Clone, PartialEq)]
pub struct IntNode { pub value: isize }

/// AST node for an unsigned integer constant
#[derive(Clone, PartialEq)]
pub struct UIntNode { pub value: usize }

/// AST node for a floating-point constant
#[derive(Clone, PartialEq)]
pub struct FloatNode { pub value: f64 }

/// AST node for a boolean constant
#[derive(Clone, PartialEq)]
pub struct BoolNode { pub value: bool }

/// AST node for a character constant
#[derive(Clone, PartialEq)]
pub struct CharNode { pub value: char }

/// AST node for a  string constant
#[derive(Clone, PartialEq)]
pub struct StringNode { pub value: String }

<commit_msg>Remove mutability<commit_after>use svm::cell::SVMCell;
use svm::slist::List;

/// Trait for AST nodes.
pub trait ASTNode {
    /// Compile this node to a list of SVM expressions
    fn compile(self)                    -> Result<SVMCell, &'static str>;

    /// Pretty-print this node
    fn prettyprint(&self)               -> String { self.print_level(0usize) }

    /// Pretty-print this node at the desired indent level
    fn print_level(&self, level: usize) -> String;
}

/// Expression
///
/// All Seax Scheme expressions are one of the following
///  + Nested S-Expressions
///  + Identifiers
///  + Lists
///  + Numbers
///     - signed int
///     - unsigned int
///     - floating-point
///  + Characters
///  + Strings
///  TODO: implement the entire Scheme 'numeric tower'
///  TODO: macros should happen
#[derive(Clone, PartialEq)]
pub enum ExprNode {
    Root(RootNode),
    SExpr(SExprNode),
    Name(NameNode),
    List(ListNode),
    IntConst(IntNode),
    UIntConst(UIntNode),
    FloatConst(FloatNode),
    BoolConst(BoolNode),
    StringConst(StringNode),
    CharConst(CharNode),
}

/// AST node for the root of a program's AST
#[derive(Clone, PartialEq)]
pub struct RootNode { pub exprs: Vec<ExprNode> }

/// AST node for an S-expression.
///
/// This includes function application, assignment,
/// function definition, et cetera...Scheme is not a complexl anguage.
#[derive(Clone, PartialEq)]
pub struct SExprNode {
    pub operator: NameNode,
    pub operands: Vec<ExprNode>,
}

/// AST node for a list literal
#[derive(Clone, PartialEq)]
pub struct ListNode { pub elements: Vec<ExprNode> }

/// AST node for an identifier
#[derive(Clone, PartialEq)]
pub struct NameNode { pub name: String }

/// AST node for an integer constant
#[derive(Clone, PartialEq)]
pub struct IntNode { pub value: isize }

/// AST node for an unsigned integer constant
#[derive(Clone, PartialEq)]
pub struct UIntNode { pub value: usize }

/// AST node for a floating-point constant
#[derive(Clone, PartialEq)]
pub struct FloatNode { pub value: f64 }

/// AST node for a boolean constant
#[derive(Clone, PartialEq)]
pub struct BoolNode { pub value: bool }

/// AST node for a character constant
#[derive(Clone, PartialEq)]
pub struct CharNode { pub value: char }

/// AST node for a  string constant
#[derive(Clone, PartialEq)]
pub struct StringNode { pub value: &'static str }

<|endoftext|>


<commit_before>use {Error, Parcel, Settings};
use hint;
use std::io::prelude::*;
use std::fmt;

/// A high level abstraction over a lower-level `Parcel`.
///
/// Automatically marshals values to and from the high level
/// and low level types for serialization.
///
/// # Example
///
/// ```
/// #[macro_use] extern crate protocol_derive;
/// extern crate protocol;
///
/// use protocol::Parcel;
///
/// #[derive(Protocol)]
/// pub struct RawPacket {
///     opcode: u8,
///     magic_number: u8,
///     payload: Vec<u8>,
///     version: (u8, u8),
/// }
///
/// #[derive(Debug, Clone)]
/// pub enum Login {
///     Success {
///         message: String,
///     },
///     Failure {
///         code: FailureCode,
///         response: String,
///     },
/// }
///
/// impl protocol::HighLevel for Login {
///     type LowLevel = RawPacket;
///
///     fn into_low_level(self) -> RawPacket {
///         match self {
///             Login::Success { message } => {
///                 RawPacket {
///                     opcode: 0,
///                     magic_number: 0xaa,
///                     payload: message.into_bytes(),
///                     version: (11, 0),
///                 }
///             },
///             Login::Failure { .. } => unimplemented!(),
///         }
///     }
///
///     fn from_low_level(low_level: RawPacket, _: &mut std::io::Read)
///         -> Result<Self, protocol::Error> {
///         match low_level.opcode {
///             0 => Ok(Login::Success { message: String::from_utf8(low_level.payload).unwrap() }),
///             1 => Ok(Login::Failure {
///                 code: FailureCode::MyDogAteMyHomework,
///                 response: String::from_utf8(low_level.payload[1..].to_owned()).unwrap() }),
///             _ => unreachable!(),
///         }
///     }
/// }
///
/// #[derive(Debug, Clone)]
/// pub enum FailureCode {
///     MyDogAteMyHomework,
/// }
///
/// let high_level = Login::Success { message: "Hi!".to_owned() };
/// assert_eq!(11, high_level.raw_bytes(&protocol::Settings::default()).unwrap().len());
/// ```
pub trait HighLevel : Clone + fmt::Debug {
    /// The lower-level representation of this type.
    type LowLevel: Parcel;

    /// Converts this high level type into its lower-level representation.
    fn into_low_level(self) -> Self::LowLevel;

    /// Creates a high-level abstraction over a lower-level value.
    ///
    /// The method has access to the reader post-parsing of the low level type.
    /// It is not necessary to use this if not needed.
    fn from_low_level(value: Self::LowLevel,
                      subsequent_reader: &mut Read) -> Result<Self, Error>;
}

impl<H> Parcel for H
    where H: HighLevel {
    const TYPE_NAME: &'static str = "HighLevel";

    fn read_field(read: &mut Read,
                  settings: &Settings,
                  hints: &mut hint::Hints) -> Result<Self, Error> {
        let low_level = H::LowLevel::read_field(read, settings, hints)?;
        H::from_low_level(low_level, read)
    }

    fn write_field(&self,
                   write: &mut Write,
                   settings: &Settings,
                   hints: &mut hint::Hints) -> Result<(), Error> {
        let low_level = self.clone().into_low_level();
        low_level.write_field(write, settings, hints)
    }
}

<commit_msg>Add extra parameters to HighLevel::from_low_level<commit_after>use {Error, Parcel, Settings};
use hint;
use std::io::prelude::*;
use std::fmt;

/// A high level abstraction over a lower-level `Parcel`.
///
/// Automatically marshals values to and from the high level
/// and low level types for serialization.
///
/// # Example
///
/// ```
/// #[macro_use] extern crate protocol_derive;
/// extern crate protocol;
///
/// use protocol::Parcel;
///
/// #[derive(Protocol)]
/// pub struct RawPacket {
///     opcode: u8,
///     magic_number: u8,
///     payload: Vec<u8>,
///     version: (u8, u8),
/// }
///
/// #[derive(Debug, Clone)]
/// pub enum Login {
///     Success {
///         message: String,
///     },
///     Failure {
///         code: FailureCode,
///         response: String,
///     },
/// }
///
/// impl protocol::HighLevel for Login {
///     type LowLevel = RawPacket;
///
///     fn into_low_level(self) -> RawPacket {
///         match self {
///             Login::Success { message } => {
///                 RawPacket {
///                     opcode: 0,
///                     magic_number: 0xaa,
///                     payload: message.into_bytes(),
///                     version: (11, 0),
///                 }
///             },
///             Login::Failure { .. } => unimplemented!(),
///         }
///     }
///
///     fn from_low_level(low_level: RawPacket,
///                       _: &mut std::io::Read,
///                       _: &protocol::Settings,
///                       _: &mut protocol::hint::Hints)
///         -> Result<Self, protocol::Error> {
///         match low_level.opcode {
///             0 => Ok(Login::Success { message: String::from_utf8(low_level.payload).unwrap() }),
///             1 => Ok(Login::Failure {
///                 code: FailureCode::MyDogAteMyHomework,
///                 response: String::from_utf8(low_level.payload[1..].to_owned()).unwrap() }),
///             _ => unreachable!(),
///         }
///     }
/// }
///
/// #[derive(Debug, Clone)]
/// pub enum FailureCode {
///     MyDogAteMyHomework,
/// }
///
/// let high_level = Login::Success { message: "Hi!".to_owned() };
/// assert_eq!(11, high_level.raw_bytes(&protocol::Settings::default()).unwrap().len());
/// ```
pub trait HighLevel : Clone + fmt::Debug {
    /// The lower-level representation of this type.
    type LowLevel: Parcel;

    /// Converts this high level type into its lower-level representation.
    fn into_low_level(self) -> Self::LowLevel;

    /// Creates a high-level abstraction over a lower-level value.
    ///
    /// The method has access to the reader post-parsing of the low level type.
    /// It is not necessary to use this if not needed.
    fn from_low_level(value: Self::LowLevel,
                      subsequent_reader: &mut Read,
                      settings: &Settings,
                      hints: &mut hint::Hints) -> Result<Self, Error>;
}

impl<H> Parcel for H
    where H: HighLevel {
    const TYPE_NAME: &'static str = "HighLevel";

    fn read_field(read: &mut Read,
                  settings: &Settings,
                  hints: &mut hint::Hints) -> Result<Self, Error> {
        let low_level = H::LowLevel::read_field(read, settings, hints)?;
        H::from_low_level(low_level, read, settings, hints)
    }

    fn write_field(&self,
                   write: &mut Write,
                   settings: &Settings,
                   hints: &mut hint::Hints) -> Result<(), Error> {
        let low_level = self.clone().into_low_level();
        low_level.write_field(write, settings, hints)
    }
}

<|endoftext|>


<commit_before>/*!
Library for reading/writing Labeled Tab-Separated Values

# Example

~~~~~~~~~~~~~~~~~~~~~~
extern mod ltsv;
use ltsv::LTSVWriter;
use ltsv::LTSVReader;

fn main() {
    let infile = io::file_reader(&Path("path/fo/file.tlsv")).get();
    for infile.read_ltsv().each |record| {
        for record.each |&(label, value)| {
            io::println(fmt!("%s: %s", *label, *value));
        }
    }
}
~~~~~~~~~~~~~~~~~~~~~~
*/
#[link(name = "ltsv",
       vers = "0.2",
       uuid = "E0EA0251-E165-4612-919F-38E89ACECBE9",
       url  = "https://github.com/tychosci/rust-ltsv/")];

#[comment = "Library for reading/writing Labeled Tab-Separated Values"];
#[license = "MIT license"];
#[crate_type = "lib"];

use core::container::Map;
use core::hashmap::linear::LinearMap;
use io::WriterUtil;

pub type Record = LinearMap<~str, ~str>;

enum ParseType {
    FieldLabel,
    FieldValue,
    Field,
    Record,
    Ltsv
}

enum ParseResult<T> {
    ParseError(~str),
    ParseOk(ParseType, T)
}

pub trait LTSVWriter {
    fn write_ltsv(&self, ltsv: &[Record]);
    fn write_ltsv_record(&self, record: &Record);
}

pub trait LTSVReader {
    fn read_ltsv(&self) -> ~[Record];
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool);
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool);
}

pub impl<T: io::Writer> LTSVWriter for T {
    fn write_ltsv(&self, ltsv: &[Record]) {
        for ltsv.each |record| {
            self.write_ltsv_record(record);
            self.write_char('\n');
        }
    }
    fn write_ltsv_record(&self, record: &Record) {
        let mut is_first = true;
        for record.each |&(k, v)| {
            if !is_first { self.write_char('\t'); }
            self.write_str(fmt!("%s:%s", *k, *v));
            if is_first { is_first = false; }
        }
    }
}

pub impl<T: io::Reader> LTSVReader for T {
    fn read_ltsv(&self) -> ~[Record] {
        let mut parser = LTSVParser::new(self);
        match parser.parse_ltsv() {
            ParseOk(_, records) => records,
            ParseError(reason) => die!(reason)
        }
    }
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() {
            match parser.parse_record() {
                ParseOk(_, record) => if !f(&record) { break; },
                ParseError(reason) => die!(reason)
            }
        }
    }
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() {
            match parser.parse_field() {
                ParseOk(_, field)  => if !f(&field) { break; },
                ParseError(reason) => die!(reason)
            }
        }
    }
}

struct LTSVParser<T> {
    priv rd: &T,
    priv cur: int
}

impl<T: io::Reader> LTSVParser<T> {
    static fn new(rd: &r/T) -> LTSVParser/&r<T> {
        let cur = rd.read_byte();
        LTSVParser { rd: rd, cur: cur }
    }

    fn eof(&self) -> bool { self.cur == -1 }

    fn bump(&mut self) {
        if !self.eof() {
            self.cur = self.rd.read_byte();
        }
    }

    fn parse_ltsv(&mut self) -> ParseResult<~[Record]> {
        let mut records = ~[];
        loop {
            match self.parse_record() {
                ParseOk(_, record) => {
                    records.push(record);
                    if self.eof() { break; }
                }
                ParseError(reason) => {
                    return ParseError(reason);
                }
            }
        }
        ParseOk(Ltsv, records)
    }

    fn parse_record(&mut self) -> ParseResult<Record> {
        let mut record = LinearMap::new();
        self.skip_whitespaces();
        loop {
            match self.parse_field() {
                ParseOk(_, (label, value)) => {
                    let saved_cur = self.cur;
                    self.bump();
                    record.insert(label, value);
                    if saved_cur == 0xa || saved_cur == -1 {
                        self.skip_whitespaces();
                        return ParseOk(Record, record);
                    }
                }
                ParseError(reason) => {
                    return ParseError(reason);
                }
            }
        }
    }

    fn parse_field(&mut self) -> ParseResult<(~str, ~str)> {
        let label = match self.parse_field_label() {
            ParseOk(_, label)  => { self.bump(); label },
            ParseError(reason) => return ParseError(reason)
        };
        match self.parse_field_value() {
            ParseOk(_, value) => {
                ParseOk(Field, (label, value))
            }
            ParseError(reason) => {
                ParseError(reason)
            }
        }
    }

    fn parse_field_label(&mut self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x30..0x39 | 0x41..0x5a | 0x61..0x7a | 0x5f |
                0x2e | 0x2d => bytes.push(self.cur as u8),
                0x3a if bytes.len() == 0 => return ParseError(~"label is empty"),
                0x3a => return ParseOk(FieldLabel, str::from_bytes(bytes)),
                -1   => return ParseError(~"EOF while parsing field label"),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    fn parse_field_value(&mut self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x01..0x08 | 0x0b | 0x0c |
                0x0e..0xff => bytes.push(self.cur as u8),
                0x0d => return self.consume_forward_LF(str::from_bytes(bytes)),
                0x0a => return ParseOk(FieldValue, str::from_bytes(bytes)),
                0x09 => return ParseOk(FieldValue, str::from_bytes(bytes)),
                -1   => return ParseOk(FieldValue, str::from_bytes(bytes)),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    fn consume_forward_LF(&mut self, rv: ~str) -> ParseResult<~str> {
        self.bump();
        if self.cur != 0x0a {
            ParseError(~"CR detected, but not provided with LF")
        } else {
            ParseOk(FieldValue, rv)
        }
    }

    fn skip_whitespaces(&mut self) {
        while char::is_whitespace(self.cur as char) {
            self.bump();
        }
    }
}

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

    #[test]
    fn test_parse_simple() {
        let records = io::with_str_reader(~"a:1\tb:2", |rd| rd.read_ltsv());
        assert records.len() == 1;
    }

    #[test]
    fn test_parse_full() {
        let s = io::with_str_writer(|wr| {
            // genzairyou
            wr.write_str(fmt!("%s:%s\t", "tofu", "豆"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "魚"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "海藻"));
            // konomi
            wr.write_str(fmt!("%s:%s\t", "tofu", "好き"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "普通"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "苦手"));
        });
        let records = io::with_str_reader(s, |rd| rd.read_ltsv());
        assert records.len() == 2;
    }

    #[test]
    fn test_parse_ltsv_trailing_nl_and_write() {
        let s = io::with_str_writer(|wr| {
            wr.write_str(fmt!("%s:%s\t", "neko", "yes"));
            wr.write_str(fmt!("%s:%s\t", "inu", "yes"));
            wr.write_str(fmt!("%s:%s\n", "tori", "yes"));
        });
        let records_1 = io::with_str_reader(s, |rd| rd.read_ltsv());
        let s2 = io::with_str_writer(|wr| wr.write_ltsv(records_1));
        let records_2 = io::with_str_reader(s2, |rd| rd.read_ltsv());
        assert records_1 == records_2;
    }
}
<commit_msg>Annotate FIXMEs to the blocker<commit_after>/*!
Library for reading/writing Labeled Tab-Separated Values

# Example

~~~~~~~~~~~~~~~~~~~~~~
extern mod ltsv;
use ltsv::LTSVWriter;
use ltsv::LTSVReader;

fn main() {
    let infile = io::file_reader(&Path("path/fo/file.tlsv")).get();
    for infile.read_ltsv().each |record| {
        for record.each |&(label, value)| {
            io::println(fmt!("%s: %s", *label, *value));
        }
    }
}
~~~~~~~~~~~~~~~~~~~~~~
*/
#[link(name = "ltsv",
       vers = "0.2",
       uuid = "E0EA0251-E165-4612-919F-38E89ACECBE9",
       url  = "https://github.com/tychosci/rust-ltsv/")];

#[comment = "Library for reading/writing Labeled Tab-Separated Values"];
#[license = "MIT license"];
#[crate_type = "lib"];

use core::container::Map;
use core::hashmap::linear::LinearMap;
use io::WriterUtil;

pub type Record = LinearMap<~str, ~str>;

enum ParseType {
    FieldLabel,
    FieldValue,
    Field,
    Record,
    Ltsv
}

enum ParseResult<T> {
    ParseError(~str),
    ParseOk(ParseType, T)
}

pub trait LTSVWriter {
    fn write_ltsv(&self, ltsv: &[Record]);
    fn write_ltsv_record(&self, record: &Record);
}

pub trait LTSVReader {
    fn read_ltsv(&self) -> ~[Record];
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool);
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool);
}

pub impl<T: io::Writer> LTSVWriter for T {
    fn write_ltsv(&self, ltsv: &[Record]) {
        for ltsv.each |record| {
            self.write_ltsv_record(record);
            self.write_char('\n');
        }
    }
    fn write_ltsv_record(&self, record: &Record) {
        let mut is_first = true;
        for record.each |&(k, v)| {
            if !is_first { self.write_char('\t'); }
            self.write_str(fmt!("%s:%s", *k, *v));
            if is_first { is_first = false; }
        }
    }
}

pub impl<T: io::Reader> LTSVReader for T {
    fn read_ltsv(&self) -> ~[Record] {
        let mut parser = LTSVParser::new(self);
        match parser.parse_ltsv() {
            ParseOk(_, records) => records,
            ParseError(reason) => die!(reason)
        }
    }
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() { // FIXME: This emits odd loaning errors.
            match parser.parse_record() {
                ParseOk(_, record) => if !f(&record) { break; },
                ParseError(reason) => die!(reason)
            }
        }
    }
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() { // FIXME: This emits odd loaning errors.
            match parser.parse_field() {
                ParseOk(_, field)  => if !f(&field) { break; },
                ParseError(reason) => die!(reason)
            }
        }
    }
}

struct LTSVParser<T> {
    priv rd: &T,
    priv cur: int
}

impl<T: io::Reader> LTSVParser<T> {
    static fn new(rd: &r/T) -> LTSVParser/&r<T> {
        let cur = rd.read_byte();
        LTSVParser { rd: rd, cur: cur }
    }

    fn eof(&self) -> bool { self.cur == -1 }

    fn bump(&mut self) {
        if !self.eof() {
            self.cur = self.rd.read_byte();
        }
    }

    fn parse_ltsv(&mut self) -> ParseResult<~[Record]> {
        let mut records = ~[];
        loop {
            match self.parse_record() {
                ParseOk(_, record) => {
                    records.push(record);
                    if self.eof() { break; }
                }
                ParseError(reason) => {
                    return ParseError(reason);
                }
            }
        }
        ParseOk(Ltsv, records)
    }

    fn parse_record(&mut self) -> ParseResult<Record> {
        let mut record = LinearMap::new();
        self.skip_whitespaces();
        loop {
            match self.parse_field() {
                ParseOk(_, (label, value)) => {
                    let saved_cur = self.cur;
                    self.bump();
                    record.insert(label, value);
                    if saved_cur == 0xa || saved_cur == -1 {
                        self.skip_whitespaces();
                        return ParseOk(Record, record);
                    }
                }
                ParseError(reason) => {
                    return ParseError(reason);
                }
            }
        }
    }

    fn parse_field(&mut self) -> ParseResult<(~str, ~str)> {
        let label = match self.parse_field_label() {
            ParseOk(_, label)  => { self.bump(); label },
            ParseError(reason) => return ParseError(reason)
        };
        match self.parse_field_value() {
            ParseOk(_, value) => {
                ParseOk(Field, (label, value))
            }
            ParseError(reason) => {
                ParseError(reason)
            }
        }
    }

    fn parse_field_label(&mut self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x30..0x39 | 0x41..0x5a | 0x61..0x7a | 0x5f |
                0x2e | 0x2d => bytes.push(self.cur as u8),
                0x3a if bytes.len() == 0 => return ParseError(~"label is empty"),
                0x3a => return ParseOk(FieldLabel, str::from_bytes(bytes)),
                -1   => return ParseError(~"EOF while parsing field label"),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    fn parse_field_value(&mut self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x01..0x08 | 0x0b | 0x0c |
                0x0e..0xff => bytes.push(self.cur as u8),
                0x0d => return self.consume_forward_LF(str::from_bytes(bytes)),
                0x0a => return ParseOk(FieldValue, str::from_bytes(bytes)),
                0x09 => return ParseOk(FieldValue, str::from_bytes(bytes)),
                -1   => return ParseOk(FieldValue, str::from_bytes(bytes)),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    fn consume_forward_LF(&mut self, rv: ~str) -> ParseResult<~str> {
        self.bump();
        if self.cur != 0x0a {
            ParseError(~"CR detected, but not provided with LF")
        } else {
            ParseOk(FieldValue, rv)
        }
    }

    fn skip_whitespaces(&mut self) {
        while char::is_whitespace(self.cur as char) {
            self.bump();
        }
    }
}

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

    #[test]
    fn test_parse_simple() {
        let records = io::with_str_reader(~"a:1\tb:2", |rd| rd.read_ltsv());
        assert records.len() == 1;
    }

    #[test]
    fn test_parse_full() {
        let s = io::with_str_writer(|wr| {
            // genzairyou
            wr.write_str(fmt!("%s:%s\t", "tofu", "豆"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "魚"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "海藻"));
            // konomi
            wr.write_str(fmt!("%s:%s\t", "tofu", "好き"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "普通"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "苦手"));
        });
        let records = io::with_str_reader(s, |rd| rd.read_ltsv());
        assert records.len() == 2;
    }

    #[test]
    fn test_parse_ltsv_trailing_nl_and_write() {
        let s = io::with_str_writer(|wr| {
            wr.write_str(fmt!("%s:%s\t", "neko", "yes"));
            wr.write_str(fmt!("%s:%s\t", "inu", "yes"));
            wr.write_str(fmt!("%s:%s\n", "tori", "yes"));
        });
        let records_1 = io::with_str_reader(s, |rd| rd.read_ltsv());
        let s2 = io::with_str_writer(|wr| wr.write_ltsv(records_1));
        let records_2 = io::with_str_reader(s2, |rd| rd.read_ltsv());
        assert records_1 == records_2;
    }
}
<|endoftext|>