1 use crate::clippy_project_root;
2 use std::fs::{File, OpenOptions};
4 use std::io::prelude::*;
5 use std::io::ErrorKind;
8 /// Creates files required to implement and test a new lint and runs `update_lints`.
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");
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"),
24 unreachable!("`pass_type` should only ever be `early` or `late`!");
28 let camel_case_name = to_camel_case(lint_name);
30 if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
31 return Err(io::Error::new(
33 format!("Could not write to test file: {}", e),
37 if let Err(e) = lint_file.write_all(
38 get_lint_file_contents(
49 return Err(io::Error::new(
51 format!("Could not write to lint file: {}", e),
56 Err(e) => Err(io::Error::new(
58 format!("Unable to create lint: {}", e),
63 fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
64 let project_root = clippy_project_root();
66 let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
67 let lint_file_path = project_root
70 .join(format!("{}.rs", lint_name));
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),
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),
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)?;
88 Ok((test_file, lint_file))
91 fn to_camel_case(name: &str) -> String {
97 [&s[0..1].to_uppercase(), &s[1..]].concat()
103 fn get_test_file_contents(lint_name: &str) -> String {
105 "#![warn(clippy::{})]
108 // test code goes here
115 fn get_lint_file_contents(
117 pass_lifetimes: &str,
119 camel_case_name: &str,
122 context_import: &str,
125 "use rustc_lint::{{{type}, {context_import}}};
126 use rustc_session::{{declare_lint_pass, declare_tool_lint}};
129 declare_clippy_lint! {{
130 /// **What it does:**
132 /// **Why is this bad?**
134 /// **Known problems:** None.
139 /// // example code where clippy issues a warning
143 /// // example code which does not raise clippy warning
147 \"default lint description\"
150 declare_lint_pass!({name_camel} => [{name_upper}]);
152 impl {type}{lifetimes} for {name_camel} {{}}
155 lifetimes=pass_lifetimes,
156 name_upper=lint_name.to_uppercase(),
157 name_camel=camel_case_name,
159 pass_import=pass_import,
160 context_import=context_import
165 fn test_camel_case() {
167 let s2 = to_camel_case(s);
168 assert_eq!(s2, "ALint");
170 let name = "a_really_long_new_lint";
171 let name2 = to_camel_case(name);
172 assert_eq!(name2, "AReallyLongNewLint");
174 let name3 = "lint__name";
175 let name4 = to_camel_case(name3);
176 assert_eq!(name4, "LintName");