22
33use clap:: { App , Arg , SubCommand } ;
44use clippy_dev:: * ;
5+ use std:: fs:: { File , OpenOptions } ;
6+ use std:: io;
7+ use std:: io:: prelude:: * ;
8+ use std:: io:: ErrorKind ;
9+ use std:: path:: PathBuf ;
510
611mod fmt;
712mod stderr_length_check;
@@ -51,6 +56,26 @@ fn main() {
5156 . help ( "Checks that util/dev update_lints has been run. Used on CI." ) ,
5257 ) ,
5358 )
59+ . subcommand (
60+ SubCommand :: with_name ( "new_lint" )
61+ . about ( "Create new lint and run util/dev update_lints" )
62+ . arg (
63+ Arg :: with_name ( "type" )
64+ . long ( "type" )
65+ . help ( "Create early or late lint" )
66+ . takes_value ( true )
67+ . possible_values ( & [ "early" , "late" ] )
68+ . required ( true ) ,
69+ )
70+ . arg (
71+ Arg :: with_name ( "name" )
72+ . short ( "n" )
73+ . long ( "name" )
74+ . help ( "Name of the new lint in snake case, ex: fn_too_long" )
75+ . takes_value ( true )
76+ . required ( true ) ,
77+ ) ,
78+ )
5479 . arg (
5580 Arg :: with_name ( "limit-stderr-length" )
5681 . long ( "limit-stderr-length" )
@@ -75,10 +100,111 @@ fn main() {
75100 update_lints ( & UpdateMode :: Change ) ;
76101 }
77102 } ,
103+ ( "new_lint" , Some ( matches) ) => {
104+ create_new_lint ( matches. value_of ( "type" ) , matches. value_of ( "name" ) ) ;
105+ } ,
78106 _ => { } ,
79107 }
80108}
81109
110+ fn project_root ( ) -> Result < PathBuf , io:: Error > {
111+ let current_dir = std:: env:: current_dir ( ) ?;
112+ for path in current_dir. ancestors ( ) {
113+ let result = std:: fs:: read_to_string ( path. join ( "Cargo.toml" ) ) ;
114+ if let Err ( err) = & result {
115+ if err. kind ( ) == io:: ErrorKind :: NotFound {
116+ continue ;
117+ }
118+ }
119+
120+ let content = result?;
121+ if content. contains ( "[package]\n name = \" clippy\" " ) {
122+ return Ok ( path. to_path_buf ( ) ) ;
123+ }
124+ }
125+ Err ( io:: Error :: new ( ErrorKind :: Other , "Unable to find project root" ) )
126+ }
127+
128+ fn open_files ( lint_name : & str ) -> Result < ( File , File ) , io:: Error > {
129+ let project_root = project_root ( ) ?;
130+
131+ let test_file_path = project_root. join ( format ! ( "tests/ui/{}.rs" , lint_name) ) ;
132+ let test_file = OpenOptions :: new ( ) . write ( true ) . create_new ( true ) . open ( test_file_path) ?;
133+
134+ let lint_file_path = project_root. join ( format ! ( "clippy_lints/src/{}.rs" , lint_name) ) ;
135+ let lint_file = OpenOptions :: new ( ) . write ( true ) . create_new ( true ) . open ( lint_file_path) ?;
136+
137+ Ok ( ( test_file, lint_file) )
138+ }
139+
140+ fn to_camel_case ( name : & str ) -> String {
141+ return name. split ( '_' )
142+ . map ( |s| [ & s[ 0 ..1 ] . to_uppercase ( ) , & s[ 1 ..] ] . concat ( ) )
143+ . collect :: < String > ( ) ;
144+ }
145+
146+ fn create_new_lint ( lint_type : Option < & str > , lint_name : Option < & str > ) {
147+ let lint_type = lint_type. expect ( "`type` argument is validated by clap" ) ;
148+ let lint_name = lint_name. expect ( "`name` argument is validated by clap" ) ;
149+
150+ match open_files ( lint_name) {
151+ Ok ( ( mut test_file, mut lint_file) ) => {
152+ let pass_type = match lint_type {
153+ "early" => "EarlyLintPass" ,
154+ "late" => "LateLintPass" ,
155+ _ => {
156+ eprintln ! ( "`pass_type` should only ever be `early` or `late`!" ) ;
157+ return ;
158+ }
159+ } ;
160+
161+ let camel_case_name = to_camel_case ( lint_name) ;
162+
163+ test_file
164+ . write_all (
165+ format ! (
166+ "#![warn(clippy::{})]
167+
168+ fn main() {{
169+ // test code goes here
170+ }}
171+ " ,
172+ lint_name
173+ )
174+ . as_bytes ( ) ,
175+ )
176+ . unwrap ( ) ;
177+
178+ lint_file
179+ . write_all (
180+ format ! (
181+ "use rustc::lint::{{LintArray, LintPass, {type}}};
182+ use rustc::declare_lint_pass;
183+ use rustc_session::declare_tool_lint;
184+
185+ declare_clippy_lint! {{
186+ pub {name_upper},
187+ nursery,
188+ \" default lint description\"
189+ }}
190+
191+ declare_lint_pass!({name_camel} => [{name_upper}]);
192+
193+ impl {type} for {name_camel} {{}}
194+ " ,
195+ type =pass_type,
196+ name_upper=lint_name. to_uppercase( ) ,
197+ name_camel=camel_case_name
198+ )
199+ . as_bytes ( ) ,
200+ )
201+ . unwrap ( ) ;
202+ update_lints ( & UpdateMode :: Change ) ;
203+ } ,
204+ Err ( e) => eprintln ! ( "Unable to create lint: {}" , e) ,
205+ }
206+ }
207+
82208fn print_lints ( ) {
83209 let lint_list = gather_all ( ) ;
84210 let usable_lints: Vec < Lint > = Lint :: usable_lints ( lint_list) . collect ( ) ;
@@ -232,3 +358,15 @@ fn update_lints(update_mode: &UpdateMode) {
232358 std:: process:: exit ( 1 ) ;
233359 }
234360}
361+
362+
363+ #[ test]
364+ fn test_camel_case ( ) {
365+ let s = "a_lint" ;
366+ let s2 = to_camel_case ( s) ;
367+ assert_eq ! ( s2, "ALint" ) ;
368+
369+ let name = "a_really_long_new_lint" ;
370+ let name2 = to_camel_case ( name) ;
371+ assert_eq ! ( name2, "AReallyLongNewLint" )
372+ }
0 commit comments