1
1
use crate :: clippy_project_root;
2
- use std:: fs:: { File , OpenOptions } ;
3
- use std:: io;
2
+ use std:: fs:: { self , OpenOptions } ;
4
3
use std:: io:: prelude:: * ;
5
- use std:: io:: ErrorKind ;
6
- use std:: path:: Path ;
4
+ use std:: io:: { self , ErrorKind } ;
5
+ use std:: path:: { Path , PathBuf } ;
6
+
7
+ struct LintData < ' a > {
8
+ pass : & ' a str ,
9
+ name : & ' a str ,
10
+ category : & ' a str ,
11
+ project_root : PathBuf ,
12
+ }
13
+
14
+ trait Context {
15
+ fn context < C : AsRef < str > > ( self , text : C ) -> Self ;
16
+ }
17
+
18
+ impl < T > Context for io:: Result < T > {
19
+ fn context < C : AsRef < str > > ( self , text : C ) -> Self {
20
+ match self {
21
+ Ok ( t) => Ok ( t) ,
22
+ Err ( e) => {
23
+ let message = format ! ( "{}: {}" , text. as_ref( ) , e) ;
24
+ Err ( io:: Error :: new ( ErrorKind :: Other , message) )
25
+ } ,
26
+ }
27
+ }
28
+ }
7
29
8
- /// Creates files required to implement and test a new lint and runs `update_lints`.
30
+ /// Creates the files required to implement and test a new lint and runs `update_lints`.
9
31
///
10
32
/// # Errors
11
33
///
12
- /// This function errors, if the files couldn't be created
13
- pub fn create ( pass : Option < & str > , lint_name : Option < & str > , category : Option < & str > ) -> Result < ( ) , io:: Error > {
14
- let pass = pass. expect ( "`pass` argument is validated by clap" ) ;
15
- let lint_name = lint_name. expect ( "`name` argument is validated by clap" ) ;
16
- let category = category. expect ( "`category` argument is validated by clap" ) ;
17
-
18
- match open_files ( lint_name) {
19
- Ok ( ( mut test_file, mut lint_file) ) => {
20
- let ( pass_type, pass_lifetimes, pass_import, context_import) = match pass {
21
- "early" => ( "EarlyLintPass" , "" , "use rustc_ast::ast::*;" , "EarlyContext" ) ,
22
- "late" => ( "LateLintPass" , "<'_, '_>" , "use rustc_hir::*;" , "LateContext" ) ,
23
- _ => {
24
- unreachable ! ( "`pass_type` should only ever be `early` or `late`!" ) ;
25
- } ,
26
- } ;
27
-
28
- let camel_case_name = to_camel_case ( lint_name) ;
29
-
30
- if let Err ( e) = test_file. write_all ( get_test_file_contents ( lint_name) . as_bytes ( ) ) {
31
- return Err ( io:: Error :: new (
32
- ErrorKind :: Other ,
33
- format ! ( "Could not write to test file: {}" , e) ,
34
- ) ) ;
35
- } ;
36
-
37
- if let Err ( e) = lint_file. write_all (
38
- get_lint_file_contents (
39
- pass_type,
40
- pass_lifetimes,
41
- lint_name,
42
- & camel_case_name,
43
- category,
44
- pass_import,
45
- context_import,
46
- )
47
- . as_bytes ( ) ,
48
- ) {
49
- return Err ( io:: Error :: new (
50
- ErrorKind :: Other ,
51
- format ! ( "Could not write to lint file: {}" , e) ,
52
- ) ) ;
53
- }
54
- Ok ( ( ) )
34
+ /// This function errors out if the files couldn't be created or written to.
35
+ pub fn create ( pass : Option < & str > , lint_name : Option < & str > , category : Option < & str > ) -> io:: Result < ( ) > {
36
+ let lint = LintData {
37
+ pass : pass. expect ( "`pass` argument is validated by clap" ) ,
38
+ name : lint_name. expect ( "`name` argument is validated by clap" ) ,
39
+ category : category. expect ( "`category` argument is validated by clap" ) ,
40
+ project_root : clippy_project_root ( ) ,
41
+ } ;
42
+
43
+ create_lint ( & lint) . context ( "Unable to create lint implementation" ) ?;
44
+ create_test ( & lint) . context ( "Unable to create a test for the new lint" )
45
+ }
46
+
47
+ fn create_lint ( lint : & LintData ) -> io:: Result < ( ) > {
48
+ let ( pass_type, pass_lifetimes, pass_import, context_import) = match lint. pass {
49
+ "early" => ( "EarlyLintPass" , "" , "use rustc_ast::ast::*;" , "EarlyContext" ) ,
50
+ "late" => ( "LateLintPass" , "<'_, '_>" , "use rustc_hir::*;" , "LateContext" ) ,
51
+ _ => {
52
+ unreachable ! ( "`pass_type` should only ever be `early` or `late`!" ) ;
55
53
} ,
56
- Err ( e) => Err ( io:: Error :: new (
57
- ErrorKind :: Other ,
58
- format ! ( "Unable to create lint: {}" , e) ,
59
- ) ) ,
60
- }
54
+ } ;
55
+
56
+ let camel_case_name = to_camel_case ( lint. name ) ;
57
+ let lint_contents = get_lint_file_contents (
58
+ pass_type,
59
+ pass_lifetimes,
60
+ lint. name ,
61
+ & camel_case_name,
62
+ lint. category ,
63
+ pass_import,
64
+ context_import,
65
+ ) ;
66
+
67
+ let lint_path = format ! ( "clippy_lints/src/{}.rs" , lint. name) ;
68
+ write_file ( lint. project_root . join ( & lint_path) , lint_contents. as_bytes ( ) )
61
69
}
62
70
63
- fn open_files ( lint_name : & str ) -> Result < ( File , File ) , io:: Error > {
64
- let project_root = clippy_project_root ( ) ;
71
+ fn create_test ( lint : & LintData ) -> io:: Result < ( ) > {
72
+ fn create_project_layout < P : Into < PathBuf > > ( lint_name : & str , location : P , case : & str , hint : & str ) -> io:: Result < ( ) > {
73
+ let mut path = location. into ( ) . join ( case) ;
74
+ fs:: create_dir ( & path) ?;
75
+ write_file ( path. join ( "Cargo.toml" ) , get_manifest_contents ( lint_name, hint) ) ?;
65
76
66
- let test_file_path = project_root. join ( "tests" ) . join ( "ui" ) . join ( format ! ( "{}.rs" , lint_name) ) ;
67
- let lint_file_path = project_root
68
- . join ( "clippy_lints" )
69
- . join ( "src" )
70
- . join ( format ! ( "{}.rs" , lint_name) ) ;
77
+ path. push ( "src" ) ;
78
+ fs:: create_dir ( & path) ?;
79
+ let header = format ! ( "// compile-flags: --crate-name={}" , lint_name) ;
80
+ write_file ( path. join ( "main.rs" ) , get_test_file_contents ( lint_name, Some ( & header) ) ) ?;
71
81
72
- if Path :: new ( & test_file_path) . exists ( ) {
73
- return Err ( io:: Error :: new (
74
- ErrorKind :: AlreadyExists ,
75
- format ! ( "test file {:?} already exists" , test_file_path) ,
76
- ) ) ;
82
+ Ok ( ( ) )
77
83
}
78
- if Path :: new ( & lint_file_path) . exists ( ) {
79
- return Err ( io:: Error :: new (
80
- ErrorKind :: AlreadyExists ,
81
- format ! ( "lint file {:?} already exists" , lint_file_path) ,
82
- ) ) ;
84
+
85
+ if lint. category == "cargo" {
86
+ let relative_test_dir = format ! ( "tests/ui-cargo/{}" , lint. name) ;
87
+ let test_dir = lint. project_root . join ( relative_test_dir) ;
88
+ fs:: create_dir ( & test_dir) ?;
89
+
90
+ create_project_layout ( lint. name , & test_dir, "fail" , "Content that triggers the lint goes here" ) ?;
91
+ create_project_layout ( lint. name , & test_dir, "pass" , "This file should not trigger the lint" )
92
+ } else {
93
+ let test_path = format ! ( "tests/ui/{}.rs" , lint. name) ;
94
+ let test_contents = get_test_file_contents ( lint. name , None ) ;
95
+ write_file ( lint. project_root . join ( test_path) , test_contents)
83
96
}
97
+ }
84
98
85
- let test_file = OpenOptions :: new ( ) . write ( true ) . create_new ( true ) . open ( test_file_path) ?;
86
- let lint_file = OpenOptions :: new ( ) . write ( true ) . create_new ( true ) . open ( lint_file_path) ?;
99
+ fn write_file < P : AsRef < Path > , C : AsRef < [ u8 ] > > ( path : P , contents : C ) -> io:: Result < ( ) > {
100
+ fn inner ( path : & Path , contents : & [ u8 ] ) -> io:: Result < ( ) > {
101
+ OpenOptions :: new ( )
102
+ . write ( true )
103
+ . create_new ( true )
104
+ . open ( path) ?
105
+ . write_all ( contents)
106
+ }
87
107
88
- Ok ( ( test_file , lint_file ) )
108
+ inner ( path . as_ref ( ) , contents . as_ref ( ) ) . context ( format ! ( "writing to file: {}" , path . as_ref ( ) . display ( ) ) )
89
109
}
90
110
91
111
fn to_camel_case ( name : & str ) -> String {
@@ -100,15 +120,35 @@ fn to_camel_case(name: &str) -> String {
100
120
. collect ( )
101
121
}
102
122
103
- fn get_test_file_contents ( lint_name : & str ) -> String {
104
- format ! (
123
+ fn get_test_file_contents ( lint_name : & str , header_commands : Option < & str > ) -> String {
124
+ let mut contents = format ! (
105
125
"#![warn(clippy::{})]
106
126
107
127
fn main() {{
108
128
// test code goes here
109
129
}}
110
130
" ,
111
131
lint_name
132
+ ) ;
133
+
134
+ if let Some ( header) = header_commands {
135
+ contents = format ! ( "{}\n {}" , header, contents) ;
136
+ }
137
+
138
+ contents
139
+ }
140
+
141
+ fn get_manifest_contents ( lint_name : & str , hint : & str ) -> String {
142
+ format ! (
143
+ r#"
144
+ # {}
145
+
146
+ [package]
147
+ name = "{}"
148
+ version = "0.1.0"
149
+ publish = false
150
+ "# ,
151
+ hint, lint_name
112
152
)
113
153
}
114
154
0 commit comments