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,103 @@ 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 create_new_lint ( lint_type : Option < & str > , lint_name : Option < & str > ) {
141+ // lint_type and lint_name are validated by clap
142+ let lint_type = lint_type. unwrap ( ) ;
143+ let lint_name = lint_name. unwrap ( ) ;
144+
145+ match open_files ( lint_name) {
146+ Ok ( ( mut test_file, mut lint_file) ) => {
147+ let pass_type = match lint_type {
148+ "early" => "EarlyLintPass" ,
149+ _ => "LateLintPass" ,
150+ } ;
151+
152+ let camel_case_name = lint_name
153+ . split ( '_' )
154+ . map ( |s| [ & s[ 0 ..1 ] . to_uppercase ( ) , & s[ 1 ..] ] . concat ( ) )
155+ . collect :: < std:: string:: String > ( ) ;
156+
157+ test_file
158+ . write_all (
159+ format ! (
160+ "#![warn(clippy::{})]
161+
162+ fn main() {{
163+ // test code goes here
164+ }}" ,
165+ lint_name
166+ )
167+ . as_bytes ( ) ,
168+ )
169+ . unwrap ( ) ;
170+
171+ lint_file
172+ . write_all (
173+ format ! (
174+ "use rustc::lint::{{LintArray, LintPass, {0}}};
175+ use rustc::declare_lint_pass;
176+ use rustc_session::declare_tool_lint;
177+
178+ declare_clippy_lint! {{
179+ pub {1},
180+ pedantic,
181+ \" Default lint description\"
182+ }}
183+
184+ declare_lint_pass!({2} => [{1}]);
185+
186+ impl {0} for {2} {{}}" ,
187+ pass_type,
188+ lint_name. to_uppercase( ) ,
189+ camel_case_name
190+ )
191+ . as_bytes ( ) ,
192+ )
193+ . unwrap ( ) ;
194+ update_lints ( & UpdateMode :: Change ) ;
195+ } ,
196+ Err ( e) => eprintln ! ( "Unable to create lint: {}" , e) ,
197+ }
198+ }
199+
82200fn print_lints ( ) {
83201 let lint_list = gather_all ( ) ;
84202 let usable_lints: Vec < Lint > = Lint :: usable_lints ( lint_list) . collect ( ) ;
0 commit comments