clippy 0.0.190

A bunch of helpful lints to avoid common pitfalls in Rust
// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![allow(unknown_lints, missing_docs_in_private_items)]

use std::collections::HashMap;
use std::process;
use std::io::{self, Write};

extern crate cargo_metadata;

use std::path::{Path, PathBuf};

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit
    --all                    Run over all packages in the current workspace

Other options are the same as `cargo rustc`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

The feature `cargo-clippy` is automatically defined for convenience. You can use
it to allow or deny lints from the code, eg.:

    #[cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))]
"#;

#[allow(print_stdout)]
fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

#[allow(print_stdout)]
fn show_version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    // Check for version and help flags even when invoked as 'cargo-clippy'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let mut manifest_path_arg = std::env::args()
        .skip(2)
        .skip_while(|val| !val.starts_with("--manifest-path"));
    let manifest_path_arg = manifest_path_arg.next().and_then(|val| {
        if val == "--manifest-path" {
            manifest_path_arg.next()
        } else if val.starts_with("--manifest-path=") {
            Some(val["--manifest-path=".len()..].to_owned())
        } else {
            None
        }
    });

    let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
        metadata
    } else {
        println!(
            "{:?}",
            cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref))
        );
        let _ = io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata.\n"));
        process::exit(101);
    };

    let manifest_path = manifest_path_arg.map(|arg| {
        PathBuf::from(arg)
            .canonicalize()
            .expect("manifest path could not be canonicalized")
    });

    let packages = if std::env::args().any(|a| a == "--all") {
        metadata.packages
    } else {
        let package_index = {
            if let Some(manifest_path) = manifest_path {
                metadata.packages.iter().position(|package| {
                    let package_manifest_path = Path::new(&package.manifest_path)
                        .canonicalize()
                        .expect("package manifest path could not be canonicalized");
                    package_manifest_path == manifest_path
                })
            } else {
                let package_manifest_paths: HashMap<_, _> = metadata
                    .packages
                    .iter()
                    .enumerate()
                    .map(|(i, package)| {
                        let package_manifest_path = Path::new(&package.manifest_path)
                            .parent()
                            .expect("could not find parent directory of package manifest")
                            .canonicalize()
                            .expect("package directory cannot be canonicalized");
                        (package_manifest_path, i)
                    })
                    .collect();

                let current_dir = std::env::current_dir()
                    .expect("CARGO_MANIFEST_DIR not set")
                    .canonicalize()
                    .expect("manifest directory cannot be canonicalized");

                let mut current_path: &Path = &current_dir;

                // This gets the most-recent parent (the one that takes the fewest `cd ..`s to
                // reach).
                loop {
                    if let Some(&package_index) = package_manifest_paths.get(current_path) {
                        break Some(package_index);
                    } else {
                        // We'll never reach the filesystem root, because to get to this point in the
                        // code
                        // the call to `cargo_metadata::metadata` must have succeeded. So it's okay to
                        // unwrap the current path's parent.
                        current_path = current_path
                            .parent()
                            .unwrap_or_else(|| panic!("could not find parent of path {}", current_path.display()));
                    }
                }
            }
        }.expect("could not find matching package");

        vec![metadata.packages.remove(package_index)]
    };

    for package in packages {
        let manifest_path = package.manifest_path;

        for target in package.targets {
            let args = std::env::args()
                .skip(2)
                .filter(|a| a != "--all" && !a.starts_with("--manifest-path="));

            let args = std::iter::once(format!("--manifest-path={}", manifest_path)).chain(args);
            if let Some(first) = target.kind.get(0) {
                if target.kind.len() > 1 || first.ends_with("lib") {
                    println!("lib: {}", target.name);
                    if let Err(code) = process(std::iter::once("--lib".to_owned()).chain(args)) {
                        std::process::exit(code);
                    }
                } else if ["bin", "example", "test", "bench"].contains(&&**first) {
                    println!("{}: {}", first, target.name);
                    if let Err(code) = process(
                        vec![format!("--{}", first), target.name]
                            .into_iter()
                            .chain(args),
                    ) {
                        std::process::exit(code);
                    }
                }
            } else {
                panic!("badly formatted cargo metadata: target::kind is an empty array");
            }
        }
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("--emit=metadata".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-clippy""#.to_owned());

    let mut path = std::env::current_exe()
        .expect("current executable path invalid")
        .with_file_name("clippy-driver");
    if cfg!(windows) {
        path.set_extension("exe");
    }
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC_WRAPPER", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}