11use crate :: clippy_project_root;
2+ use indoc:: indoc;
23use std:: fs:: { self , OpenOptions } ;
34use std:: io:: prelude:: * ;
45use std:: io:: { self , ErrorKind } ;
@@ -32,37 +33,20 @@ impl<T> Context for io::Result<T> {
3233/// # Errors
3334///
3435/// 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+ pub fn create ( pass : Option < & str > , lint_name : Option < & str > , category : Option < & str > , msrv : bool ) -> io:: Result < ( ) > {
3637 let lint = LintData {
3738 pass : pass. expect ( "`pass` argument is validated by clap" ) ,
3839 name : lint_name. expect ( "`name` argument is validated by clap" ) ,
3940 category : category. expect ( "`category` argument is validated by clap" ) ,
4041 project_root : clippy_project_root ( ) ,
4142 } ;
4243
43- create_lint ( & lint) . context ( "Unable to create lint implementation" ) ?;
44+ create_lint ( & lint, msrv ) . context ( "Unable to create lint implementation" ) ?;
4445 create_test ( & lint) . context ( "Unable to create a test for the new lint" )
4546}
4647
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`!" ) ;
53- } ,
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- ) ;
48+ fn create_lint ( lint : & LintData < ' _ > , enable_msrv : bool ) -> io:: Result < ( ) > {
49+ let lint_contents = get_lint_file_contents ( lint, enable_msrv) ;
6650
6751 let lint_path = format ! ( "clippy_lints/src/{}.rs" , lint. name) ;
6852 write_file ( lint. project_root . join ( & lint_path) , lint_contents. as_bytes ( ) )
@@ -122,12 +106,13 @@ fn to_camel_case(name: &str) -> String {
122106
123107fn get_test_file_contents ( lint_name : & str , header_commands : Option < & str > ) -> String {
124108 let mut contents = format ! (
125- "#![warn(clippy::{})]
109+ indoc! { "
110+ #![warn(clippy::{})]
126111
127- fn main() {{
128- // test code goes here
129- }}
130- " ,
112+ fn main() {{
113+ // test code goes here
114+ }}
115+ " } ,
131116 lint_name
132117 ) ;
133118
@@ -140,64 +125,143 @@ fn main() {{
140125
141126fn get_manifest_contents ( lint_name : & str , hint : & str ) -> String {
142127 format ! (
143- r#"
144- # {}
128+ indoc! { r#"
129+ # {}
145130
146- [package]
147- name = "{}"
148- version = "0.1.0"
149- publish = false
131+ [package]
132+ name = "{}"
133+ version = "0.1.0"
134+ publish = false
150135
151- [workspace]
152- "# ,
136+ [workspace]
137+ "# } ,
153138 hint, lint_name
154139 )
155140}
156141
157- fn get_lint_file_contents (
158- pass_type : & str ,
159- pass_lifetimes : & str ,
160- lint_name : & str ,
161- camel_case_name : & str ,
162- category : & str ,
163- pass_import : & str ,
164- context_import : & str ,
165- ) -> String {
166- format ! (
167- "use rustc_lint::{{{type}, {context_import}}};
168- use rustc_session::{{declare_lint_pass, declare_tool_lint}};
169- {pass_import}
170-
171- declare_clippy_lint! {{
172- /// ### What it does
173- ///
174- /// ### Why is this bad?
175- ///
176- /// ### Example
177- /// ```rust
178- /// // example code where clippy issues a warning
179- /// ```
180- /// Use instead:
181- /// ```rust
182- /// // example code which does not raise clippy warning
183- /// ```
184- pub {name_upper},
185- {category},
186- \" default lint description\"
187- }}
188-
189- declare_lint_pass!({name_camel} => [{name_upper}]);
190-
191- impl {type}{lifetimes} for {name_camel} {{}}
192- " ,
193- type =pass_type,
194- lifetimes=pass_lifetimes,
195- name_upper=lint_name. to_uppercase( ) ,
196- name_camel=camel_case_name,
197- category=category,
198- pass_import=pass_import,
199- context_import=context_import
200- )
142+ fn get_lint_file_contents ( lint : & LintData < ' _ > , enable_msrv : bool ) -> String {
143+ let mut result = String :: new ( ) ;
144+
145+ let ( pass_type, pass_lifetimes, pass_import, context_import) = match lint. pass {
146+ "early" => ( "EarlyLintPass" , "" , "use rustc_ast::ast::*;" , "EarlyContext" ) ,
147+ "late" => ( "LateLintPass" , "<'_>" , "use rustc_hir::*;" , "LateContext" ) ,
148+ _ => {
149+ unreachable ! ( "`pass_type` should only ever be `early` or `late`!" ) ;
150+ } ,
151+ } ;
152+
153+ let lint_name = lint. name ;
154+ let pass_name = lint. pass ;
155+ let category = lint. category ;
156+ let name_camel = to_camel_case ( lint. name ) ;
157+ let name_upper = lint_name. to_uppercase ( ) ;
158+
159+ result. push_str ( & if enable_msrv {
160+ format ! (
161+ indoc! { "
162+ use clippy_utils::msrvs;
163+ {pass_import}
164+ use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
165+ use rustc_semver::RustcVersion;
166+ use rustc_session::{{declare_tool_lint, impl_lint_pass}};
167+
168+ " } ,
169+ pass_type = pass_type,
170+ pass_import = pass_import,
171+ context_import = context_import,
172+ )
173+ } else {
174+ format ! (
175+ indoc! { "
176+ {pass_import}
177+ use rustc_lint::{{{context_import}, {pass_type}}};
178+ use rustc_session::{{declare_lint_pass, declare_tool_lint}};
179+
180+ " } ,
181+ pass_import = pass_import,
182+ pass_type = pass_type,
183+ context_import = context_import
184+ )
185+ } ) ;
186+
187+ result. push_str ( & format ! (
188+ indoc! { "
189+ declare_clippy_lint! {{
190+ /// ### What it does
191+ ///
192+ /// ### Why is this bad?
193+ ///
194+ /// ### Example
195+ /// ```rust
196+ /// // example code where clippy issues a warning
197+ /// ```
198+ /// Use instead:
199+ /// ```rust
200+ /// // example code which does not raise clippy warning
201+ /// ```
202+ pub {name_upper},
203+ {category},
204+ \" default lint description\"
205+ }}
206+ " } ,
207+ name_upper = name_upper,
208+ category = category,
209+ ) ) ;
210+
211+ result. push_str ( & if enable_msrv {
212+ format ! (
213+ indoc! { "
214+ pub struct {name_camel} {{
215+ msrv: Option<RustcVersion>,
216+ }}
217+
218+ impl {name_camel} {{
219+ #[must_use]
220+ pub fn new(msrv: Option<RustcVersion>) -> Self {{
221+ Self {{ msrv }}
222+ }}
223+ }}
224+
225+ impl_lint_pass!({name_camel} => [{name_upper}]);
226+
227+ impl {pass_type}{pass_lifetimes} for {name_camel} {{
228+ extract_msrv_attr!({context_import});
229+ }}
230+
231+ // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
232+ // e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv)));
233+ // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
234+ // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
235+ // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
236+ " } ,
237+ pass_type = pass_type,
238+ pass_lifetimes = pass_lifetimes,
239+ pass_name = pass_name,
240+ name_upper = name_upper,
241+ name_camel = name_camel,
242+ module_name = lint_name,
243+ context_import = context_import,
244+ )
245+ } else {
246+ format ! (
247+ indoc! { "
248+ declare_lint_pass!({name_camel} => [{name_upper}]);
249+
250+ impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
251+ //
252+ // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
253+ // e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel}));
254+ " } ,
255+ pass_type = pass_type,
256+ pass_lifetimes = pass_lifetimes,
257+ pass_name = pass_name,
258+ name_upper = name_upper,
259+ name_camel = name_camel,
260+ module_name = lint_name,
261+ )
262+ } ) ;
263+
264+ result
201265}
202266
203267#[ test]
0 commit comments