2016-05-24 16:25:25 +00:00
// error-pattern:yummy
#![ feature(box_syntax) ]
#![ feature(rustc_private) ]
2016-08-23 16:09:37 +00:00
#![ allow(unknown_lints, missing_docs_in_private_items) ]
2016-06-28 13:54:23 +00:00
extern crate clippy_lints ;
2016-05-24 16:25:25 +00:00
extern crate getopts ;
extern crate rustc ;
2016-06-28 13:54:23 +00:00
extern crate rustc_driver ;
extern crate rustc_errors ;
2016-05-24 16:25:25 +00:00
extern crate rustc_plugin ;
2016-06-28 13:54:23 +00:00
extern crate syntax ;
2016-05-24 16:25:25 +00: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-17 03:48:15 +00:00
use syntax ::ast ;
2016-05-24 16:25:25 +00:00
2016-05-30 10:47:04 +00:00
use clippy_lints ::utils ::cargo ;
2016-05-27 13:31:19 +00:00
2016-07-04 11:33:48 +00:00
struct ClippyCompilerCalls {
default : RustcDefaultCalls ,
run_lints : bool ,
2016-05-24 16:25:25 +00:00
}
impl ClippyCompilerCalls {
2016-07-04 11:33:48 +00:00
fn new ( run_lints : bool ) -> Self {
ClippyCompilerCalls {
default : RustcDefaultCalls ,
run_lints : run_lints ,
}
2016-05-24 16:25:25 +00:00
}
}
impl < ' a > CompilerCalls < ' a > for ClippyCompilerCalls {
fn early_callback ( & mut self ,
matches : & getopts ::Matches ,
sopts : & config ::Options ,
2016-08-16 22:21:58 +00:00
cfg : & ast ::CrateConfig ,
2016-06-28 13:54:23 +00:00
descriptions : & rustc_errors ::registry ::Registry ,
2016-05-24 16:25:25 +00:00
output : ErrorOutputType )
-> Compilation {
2016-08-16 22:21:58 +00:00
self . default . early_callback ( matches , sopts , cfg , descriptions , output )
2016-05-24 16:25:25 +00:00
}
fn no_input ( & mut self ,
matches : & getopts ::Matches ,
sopts : & config ::Options ,
2016-08-16 22:21:58 +00:00
cfg : & ast ::CrateConfig ,
2016-05-24 16:25:25 +00:00
odir : & Option < PathBuf > ,
ofile : & Option < PathBuf > ,
2016-06-28 13:54:23 +00:00
descriptions : & rustc_errors ::registry ::Registry )
2016-05-24 16:25:25 +00:00
-> Option < ( Input , Option < PathBuf > ) > {
2016-08-16 22:21:58 +00:00
self . default . no_input ( matches , sopts , cfg , odir , ofile , descriptions )
2016-05-24 16:25:25 +00:00
}
fn late_callback ( & mut self ,
matches : & getopts ::Matches ,
sess : & Session ,
2016-08-16 22:21:58 +00:00
cfg : & ast ::CrateConfig ,
2016-05-24 16:25:25 +00:00
input : & Input ,
odir : & Option < PathBuf > ,
ofile : & Option < PathBuf > )
-> Compilation {
2016-08-16 22:21:58 +00:00
self . default . late_callback ( matches , sess , cfg , input , odir , ofile )
2016-05-24 16:25:25 +00:00
}
fn build_controller ( & mut self , sess : & Session , matches : & getopts ::Matches ) -> driver ::CompileController < ' a > {
2016-07-04 11:33:48 +00: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 17:04:38 +00: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 11:33:48 +00: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 16:25:25 +00:00
2016-07-04 11:33:48 +00:00
for ( name , to ) in lint_groups {
ls . register_group ( Some ( sess ) , true , name , to ) ;
}
2016-05-24 16:25:25 +00:00
2016-07-04 11:33:48 +00: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 16:25:25 +00:00
control
}
}
use std ::path ::Path ;
pub fn main ( ) {
use std ::env ;
if env ::var ( " CLIPPY_DOGFOOD " ) . map ( | _ | true ) . unwrap_or ( false ) {
panic! ( " yummy " ) ;
}
let dep_path = env ::current_dir ( ) . expect ( " current dir is not readable " ) . join ( " target " ) . join ( " debug " ) . join ( " deps " ) ;
let home = option_env! ( " RUSTUP_HOME " ) . or ( option_env! ( " MULTIRUST_HOME " ) ) ;
let toolchain = option_env! ( " RUSTUP_TOOLCHAIN " ) . or ( option_env! ( " MULTIRUST_TOOLCHAIN " ) ) ;
2016-06-05 23:53:21 +00:00
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-05-24 16:25:25 +00:00
} ;
if let Some ( " clippy " ) = std ::env ::args ( ) . nth ( 1 ) . as_ref ( ) . map ( AsRef ::as_ref ) {
2016-07-04 11:33:48 +00:00
// this arm is executed on the initial call to `cargo clippy`
2016-10-23 01:15:42 +00:00
let manifest_path_arg = std ::env ::args ( ) . skip ( 2 ) . find ( | val | val . starts_with ( " --manifest-path= " ) ) ;
2016-10-23 19:24:16 +00:00
let mut metadata = cargo ::metadata ( manifest_path_arg . as_ref ( ) . map ( AsRef ::as_ref ) ) . expect ( " could not obtain cargo metadata " ) ;
2016-05-27 13:31:19 +00:00
assert_eq! ( metadata . version , 1 ) ;
2016-10-23 01:15:42 +00: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 19:29:33 +00:00
} else {
let current_dir = current_dir . as_ref ( ) . expect ( " could not read current directory " ) ;
2016-10-23 01:15:42 +00: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 13:31:19 +00:00
let args = std ::env ::args ( ) . skip ( 2 ) ;
2016-06-02 15:39:28 +00:00
if let Some ( first ) = target . kind . get ( 0 ) {
if target . kind . len ( ) > 1 | | first . ends_with ( " lib " ) {
2016-06-06 09:28:09 +00:00
if let Err ( code ) = process ( std ::iter ::once ( " --lib " . to_owned ( ) ) . chain ( args ) , & dep_path , & sys_root ) {
std ::process ::exit ( code ) ;
}
2016-08-17 15:43:21 +00:00
} else if [ " bin " , " example " , " test " , " bench " ] . contains ( & & * * first ) {
if let Err ( code ) = process ( vec! [ format! ( " -- {} " , first ) , target . name ] . into_iter ( ) . chain ( args ) , & dep_path , & sys_root ) {
2016-08-17 15:04:21 +00:00
std ::process ::exit ( code ) ;
}
2016-06-02 15:39:28 +00:00
}
} else {
panic! ( " badly formatted cargo metadata: target::kind is an empty array " ) ;
2016-05-27 13:31:19 +00:00
}
2016-05-24 16:25:25 +00:00
}
} else {
2016-07-04 11:33:48 +00:00
// this arm is executed when cargo-clippy runs `cargo rustc` with the `RUSTC` env var set to itself
// this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
// without having to pass --sysroot or anything
2016-06-29 11:58:07 +00:00
let args : Vec < String > = if env ::args ( ) . any ( | s | s = = " --sysroot " ) {
2016-05-24 16:25:25 +00:00
env ::args ( ) . collect ( )
} else {
env ::args ( ) . chain ( Some ( " --sysroot " . to_owned ( ) ) ) . chain ( Some ( sys_root ) ) . collect ( )
} ;
2016-07-04 11:33:48 +00:00
// this check ensures that dependencies are built but not linted and the final crate is
// linted but not built
let mut ccc = ClippyCompilerCalls ::new ( env ::args ( ) . any ( | s | s = = " -Zno-trans " ) ) ;
2016-09-30 13:33:24 +00:00
let ( result , _ ) = rustc_driver ::run_compiler ( & args , & mut ccc , None , None ) ;
2016-05-24 16:25:25 +00:00
if let Err ( err_count ) = result {
if err_count > 0 {
std ::process ::exit ( 1 ) ;
}
}
}
}
2016-06-06 09:28:09 +00:00
fn process < P , I > ( old_args : I , dep_path : P , sysroot : & str ) -> Result < ( ) , i32 >
2016-06-05 23:42:39 +00:00
where P : AsRef < Path > ,
I : Iterator < Item = String >
{
2016-05-24 16:25:25 +00: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 ( String ::from ( " --sysroot " ) ) ;
2016-05-27 13:31:19 +00:00
args . push ( sysroot . to_owned ( ) ) ;
2016-05-24 16:25:25 +00:00
args . push ( " -Zno-trans " . to_owned ( ) ) ;
2016-05-27 13:31:19 +00: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 09:28:09 +00:00
if exit_status . success ( ) {
Ok ( ( ) )
} else {
2016-06-06 14:43:58 +00:00
Err ( exit_status . code ( ) . unwrap_or ( - 1 ) )
2016-05-27 13:31:19 +00:00
}
2016-05-24 16:25:25 +00:00
}