2016-05-24 11:25:25 -05:00
// error-pattern:yummy
#![ feature(box_syntax) ]
#![ feature(rustc_private) ]
2016-10-24 08:31:11 -05:00
#![ feature(static_in_const) ]
2016-05-24 11:25:25 -05:00
2016-08-23 11:09:37 -05:00
#![ allow(unknown_lints, missing_docs_in_private_items) ]
2016-06-28 08:54:23 -05:00
extern crate clippy_lints ;
2016-05-24 11:25:25 -05:00
extern crate getopts ;
extern crate rustc ;
2016-06-28 08:54:23 -05:00
extern crate rustc_driver ;
extern crate rustc_errors ;
2016-05-24 11:25:25 -05:00
extern crate rustc_plugin ;
2016-06-28 08:54:23 -05:00
extern crate syntax ;
2016-05-24 11:25:25 -05:00
use rustc_driver ::{ driver , CompilerCalls , RustcDefaultCalls , Compilation } ;
use rustc ::session ::{ config , Session } ;
use rustc ::session ::config ::{ Input , ErrorOutputType } ;
use std ::path ::PathBuf ;
use std ::process ::Command ;
2016-08-16 22:48:15 -05:00
use syntax ::ast ;
2016-05-24 11:25:25 -05:00
2016-05-30 05:47:04 -05:00
use clippy_lints ::utils ::cargo ;
2016-05-27 08:31:19 -05:00
2016-07-04 06:33:48 -05:00
struct ClippyCompilerCalls {
default : RustcDefaultCalls ,
run_lints : bool ,
2016-05-24 11:25:25 -05:00
}
impl ClippyCompilerCalls {
2016-07-04 06:33:48 -05:00
fn new ( run_lints : bool ) -> Self {
ClippyCompilerCalls {
default : RustcDefaultCalls ,
run_lints : run_lints ,
}
2016-05-24 11:25:25 -05:00
}
}
impl < ' a > CompilerCalls < ' a > for ClippyCompilerCalls {
fn early_callback ( & mut self ,
matches : & getopts ::Matches ,
sopts : & config ::Options ,
2016-08-16 17:21:58 -05:00
cfg : & ast ::CrateConfig ,
2016-06-28 08:54:23 -05:00
descriptions : & rustc_errors ::registry ::Registry ,
2016-05-24 11:25:25 -05:00
output : ErrorOutputType )
-> Compilation {
2016-08-16 17:21:58 -05:00
self . default . early_callback ( matches , sopts , cfg , descriptions , output )
2016-05-24 11:25:25 -05:00
}
fn no_input ( & mut self ,
matches : & getopts ::Matches ,
sopts : & config ::Options ,
2016-08-16 17:21:58 -05:00
cfg : & ast ::CrateConfig ,
2016-05-24 11:25:25 -05:00
odir : & Option < PathBuf > ,
ofile : & Option < PathBuf > ,
2016-06-28 08:54:23 -05:00
descriptions : & rustc_errors ::registry ::Registry )
2016-05-24 11:25:25 -05:00
-> Option < ( Input , Option < PathBuf > ) > {
2016-08-16 17:21:58 -05:00
self . default . no_input ( matches , sopts , cfg , odir , ofile , descriptions )
2016-05-24 11:25:25 -05:00
}
fn late_callback ( & mut self ,
matches : & getopts ::Matches ,
sess : & Session ,
input : & Input ,
odir : & Option < PathBuf > ,
ofile : & Option < PathBuf > )
-> Compilation {
2016-11-02 14:27:42 -05:00
self . default . late_callback ( matches , sess , input , odir , ofile )
2016-05-24 11:25:25 -05:00
}
fn build_controller ( & mut self , sess : & Session , matches : & getopts ::Matches ) -> driver ::CompileController < ' a > {
2016-07-04 06:33:48 -05:00
let mut control = self . default . build_controller ( sess , matches ) ;
if self . run_lints {
let old = std ::mem ::replace ( & mut control . after_parse . callback , box | _ | { } ) ;
control . after_parse . callback = Box ::new ( move | state | {
{
2016-09-06 12:04:38 -05:00
let mut registry = rustc_plugin ::registry ::Registry ::new ( state . session , state . krate . as_ref ( ) . expect ( " at this compilation stage the krate must be parsed " ) . span ) ;
2016-07-04 06:33:48 -05:00
registry . args_hidden = Some ( Vec ::new ( ) ) ;
clippy_lints ::register_plugins ( & mut registry ) ;
let rustc_plugin ::registry ::Registry { early_lint_passes ,
late_lint_passes ,
lint_groups ,
llvm_passes ,
attributes ,
mir_passes ,
.. } = registry ;
let sess = & state . session ;
let mut ls = sess . lint_store . borrow_mut ( ) ;
for pass in early_lint_passes {
ls . register_early_pass ( Some ( sess ) , true , pass ) ;
}
for pass in late_lint_passes {
ls . register_late_pass ( Some ( sess ) , true , pass ) ;
}
2016-05-24 11:25:25 -05:00
2016-07-04 06:33:48 -05:00
for ( name , to ) in lint_groups {
ls . register_group ( Some ( sess ) , true , name , to ) ;
}
2016-05-24 11:25:25 -05:00
2016-07-04 06:33:48 -05:00
sess . plugin_llvm_passes . borrow_mut ( ) . extend ( llvm_passes ) ;
sess . mir_passes . borrow_mut ( ) . extend ( mir_passes ) ;
sess . plugin_attributes . borrow_mut ( ) . extend ( attributes ) ;
}
old ( state ) ;
} ) ;
}
2016-05-24 11:25:25 -05:00
control
}
}
use std ::path ::Path ;
2016-10-24 09:04:00 -05:00
const CARGO_CLIPPY_HELP : & str = r #" Checks a package to catch common mistakes and improve your Rust code.
2016-10-24 08:31:11 -05:00
Usage :
cargo clippy [ options ] [ - - ] [ < opts > .. . ]
Common options :
- h , - - help Print this message
- - features Features to compile for the package
2016-11-08 06:54:08 -06:00
- V , - - version Print version info and exit
2016-10-24 08:31:11 -05:00
Other options are the same as ` cargo rustc ` .
2016-10-25 08:09:56 -05:00
To allow or deny a lint from the command line you can use ` cargo clippy - - `
with :
2016-10-24 08:31:11 -05:00
- W - - warn OPT Set lint warnings
- A - - allow OPT Set lint allowed
- D - - deny OPT Set lint denied
2016-10-24 09:04:00 -05:00
- F - - forbid OPT Set lint forbidden
2016-10-25 08:09:56 -05:00
The feature ` cargo - clippy ` is automatically defined for convenience . You can use
it to allow or deny lints from the code , eg . :
2016-10-24 09:04:00 -05:00
#[ cfg_attr(feature = " cargo-clippy " , allow(needless_lifetimes)) ]
" #;
2016-10-24 08:31:11 -05:00
2016-10-24 09:29:36 -05:00
#[ allow(print_stdout) ]
fn show_help ( ) {
println! ( " {} " , CARGO_CLIPPY_HELP ) ;
}
2016-05-24 11:25:25 -05:00
pub fn main ( ) {
use std ::env ;
if env ::var ( " CLIPPY_DOGFOOD " ) . map ( | _ | true ) . unwrap_or ( false ) {
panic! ( " yummy " ) ;
}
2016-11-08 06:54:08 -06:00
// 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 " ) {
println! ( " {} " , env! ( " CARGO_PKG_VERSION " ) ) ;
return ;
}
2016-05-24 11:25:25 -05:00
let dep_path = env ::current_dir ( ) . expect ( " current dir is not readable " ) . join ( " target " ) . join ( " debug " ) . join ( " deps " ) ;
if let Some ( " clippy " ) = std ::env ::args ( ) . nth ( 1 ) . as_ref ( ) . map ( AsRef ::as_ref ) {
2016-07-04 06:33:48 -05:00
// this arm is executed on the initial call to `cargo clippy`
2016-10-24 08:31:11 -05:00
2016-10-22 20:15:42 -05:00
let manifest_path_arg = std ::env ::args ( ) . skip ( 2 ) . find ( | val | val . starts_with ( " --manifest-path= " ) ) ;
2016-10-23 14:24:16 -05:00
let mut metadata = cargo ::metadata ( manifest_path_arg . as_ref ( ) . map ( AsRef ::as_ref ) ) . expect ( " could not obtain cargo metadata " ) ;
2016-10-24 08:31:11 -05:00
2016-05-27 08:31:19 -05:00
assert_eq! ( metadata . version , 1 ) ;
2016-10-22 20:15:42 -05:00
let manifest_path = manifest_path_arg . map ( | arg | PathBuf ::from ( Path ::new ( & arg [ " --manifest-path= " . len ( ) .. ] ) ) ) ;
let current_dir = std ::env ::current_dir ( ) ;
let package_index = metadata . packages . iter ( )
. position ( | package | {
let package_manifest_path = Path ::new ( & package . manifest_path ) ;
if let Some ( ref manifest_path ) = manifest_path {
package_manifest_path = = manifest_path
2016-10-23 14:29:33 -05:00
} else {
let current_dir = current_dir . as_ref ( ) . expect ( " could not read current directory " ) ;
2016-10-22 20:15:42 -05:00
let package_manifest_directory = package_manifest_path . parent ( ) . expect ( " could not find parent directory of package manifest " ) ;
package_manifest_directory = = current_dir
}
} )
. expect ( " could not find matching package " ) ;
let package = metadata . packages . remove ( package_index ) ;
for target in package . targets {
2016-05-27 08:31:19 -05:00
let args = std ::env ::args ( ) . skip ( 2 ) ;
2016-06-02 10:39:28 -05:00
if let Some ( first ) = target . kind . get ( 0 ) {
if target . kind . len ( ) > 1 | | first . ends_with ( " lib " ) {
2016-11-08 04:35:26 -06:00
if let Err ( code ) = process ( std ::iter ::once ( " --lib " . to_owned ( ) ) . chain ( args ) , & dep_path ) {
2016-06-06 04:28:09 -05:00
std ::process ::exit ( code ) ;
}
2016-08-17 10:43:21 -05:00
} else if [ " bin " , " example " , " test " , " bench " ] . contains ( & & * * first ) {
2016-11-08 04:35:26 -06:00
if let Err ( code ) = process ( vec! [ format! ( " -- {} " , first ) , target . name ] . into_iter ( ) . chain ( args ) , & dep_path ) {
2016-08-17 10:04:21 -05:00
std ::process ::exit ( code ) ;
}
2016-06-02 10:39:28 -05:00
}
} else {
panic! ( " badly formatted cargo metadata: target::kind is an empty array " ) ;
2016-05-27 08:31:19 -05:00
}
2016-05-24 11:25:25 -05:00
}
} else {
2016-07-04 06:33:48 -05:00
// this arm is executed when cargo-clippy runs `cargo rustc` with the `RUSTC` env var set to itself
2016-11-08 04:35:26 -06:00
let home = option_env! ( " RUSTUP_HOME " ) . or ( option_env! ( " MULTIRUST_HOME " ) ) ;
let toolchain = option_env! ( " RUSTUP_TOOLCHAIN " ) . or ( option_env! ( " MULTIRUST_TOOLCHAIN " ) ) ;
let sys_root = if let ( Some ( home ) , Some ( toolchain ) ) = ( home , toolchain ) {
format! ( " {} /toolchains/ {} " , home , toolchain )
} else {
option_env! ( " SYSROOT " )
. map ( | s | s . to_owned ( ) )
. or ( Command ::new ( " rustc " )
. arg ( " --print " )
. arg ( " sysroot " )
. output ( )
. ok ( )
. and_then ( | out | String ::from_utf8 ( out . stdout ) . ok ( ) )
. map ( | s | s . trim ( ) . to_owned ( ) ) )
. expect ( " need to specify SYSROOT env var during clippy compilation, or use rustup or multirust " )
} ;
2016-07-04 06:33:48 -05:00
// this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
// without having to pass --sysroot or anything
2016-10-24 09:04:00 -05:00
let mut args : Vec < String > = if env ::args ( ) . any ( | s | s = = " --sysroot " ) {
2016-05-24 11:25:25 -05:00
env ::args ( ) . collect ( )
} else {
env ::args ( ) . chain ( Some ( " --sysroot " . to_owned ( ) ) ) . chain ( Some ( sys_root ) ) . collect ( )
} ;
2016-10-24 09:04:00 -05:00
2016-07-04 06:33:48 -05:00
// this check ensures that dependencies are built but not linted and the final crate is
// linted but not built
2016-10-24 09:04:00 -05:00
args . extend_from_slice ( & [ " --cfg " . to_owned ( ) , r # "feature="cargo-clippy""# . to_owned ( ) ] ) ;
2016-07-04 06:33:48 -05:00
let mut ccc = ClippyCompilerCalls ::new ( env ::args ( ) . any ( | s | s = = " -Zno-trans " ) ) ;
2016-09-30 08:33:24 -05:00
let ( result , _ ) = rustc_driver ::run_compiler ( & args , & mut ccc , None , None ) ;
2016-05-24 11:25:25 -05:00
if let Err ( err_count ) = result {
if err_count > 0 {
std ::process ::exit ( 1 ) ;
}
}
}
}
2016-11-08 04:35:26 -06:00
fn process < P , I > ( old_args : I , dep_path : P ) -> Result < ( ) , i32 >
2016-06-05 18:42:39 -05:00
where P : AsRef < Path > ,
I : Iterator < Item = String >
{
2016-05-24 11:25:25 -05:00
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 ( " -L " . to_owned ( ) ) ;
args . push ( dep_path . as_ref ( ) . to_string_lossy ( ) . into_owned ( ) ) ;
args . push ( " -Zno-trans " . to_owned ( ) ) ;
2016-10-24 09:04:00 -05:00
args . push ( " --cfg " . to_owned ( ) ) ;
args . push ( r # "feature="cargo-clippy""# . to_owned ( ) ) ;
2016-05-27 08:31:19 -05:00
let path = std ::env ::current_exe ( ) . expect ( " current executable path invalid " ) ;
let exit_status = std ::process ::Command ::new ( " cargo " )
. args ( & args )
. env ( " RUSTC " , path )
. spawn ( ) . expect ( " could not run cargo " )
. wait ( ) . expect ( " failed to wait for cargo? " ) ;
2016-06-06 04:28:09 -05:00
if exit_status . success ( ) {
Ok ( ( ) )
} else {
2016-06-06 09:43:58 -05:00
Err ( exit_status . code ( ) . unwrap_or ( - 1 ) )
2016-05-27 08:31:19 -05:00
}
2016-05-24 11:25:25 -05:00
}