2
2
3
3
use clap:: { App , Arg , SubCommand } ;
4
4
use 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 ;
5
10
6
11
mod fmt;
7
12
mod stderr_length_check;
@@ -51,6 +56,26 @@ fn main() {
51
56
. help ( "Checks that util/dev update_lints has been run. Used on CI." ) ,
52
57
) ,
53
58
)
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
+ )
54
79
. arg (
55
80
Arg :: with_name ( "limit-stderr-length" )
56
81
. long ( "limit-stderr-length" )
@@ -75,10 +100,111 @@ fn main() {
75
100
update_lints ( & UpdateMode :: Change ) ;
76
101
}
77
102
} ,
103
+ ( "new_lint" , Some ( matches) ) => {
104
+ create_new_lint ( matches. value_of ( "type" ) , matches. value_of ( "name" ) ) ;
105
+ } ,
78
106
_ => { } ,
79
107
}
80
108
}
81
109
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
+
82
208
fn print_lints ( ) {
83
209
let lint_list = gather_all ( ) ;
84
210
let usable_lints: Vec < Lint > = Lint :: usable_lints ( lint_list) . collect ( ) ;
@@ -232,3 +358,15 @@ fn update_lints(update_mode: &UpdateMode) {
232
358
std:: process:: exit ( 1 ) ;
233
359
}
234
360
}
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