From aa031e91f4f809933eb967edda256ebf6b8bf4ea Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 20 Oct 2020 21:29:31 +0200 Subject: add completions for clippy lint in attributes Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- xtask/src/codegen.rs | 6 +- xtask/src/codegen/gen_lint_completions.rs | 113 ++++++++++++++++++++++++++++++ xtask/tests/tidy.rs | 10 ++- 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 xtask/src/codegen/gen_lint_completions.rs (limited to 'xtask') diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index afa703471..adea053b6 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -9,7 +9,7 @@ mod gen_syntax; mod gen_parser_tests; mod gen_assists_docs; mod gen_feature_docs; -mod gen_features; +mod gen_lint_completions; mod gen_diagnostic_docs; use std::{ @@ -24,7 +24,7 @@ pub use self::{ gen_assists_docs::{generate_assists_docs, generate_assists_tests}, gen_diagnostic_docs::generate_diagnostic_docs, gen_feature_docs::generate_feature_docs, - gen_features::generate_features, + gen_lint_completions::generate_lint_completions, gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, }; @@ -42,7 +42,7 @@ pub struct CodegenCmd { impl CodegenCmd { pub fn run(self) -> Result<()> { if self.features { - generate_features(Mode::Overwrite)?; + generate_lint_completions(Mode::Overwrite)?; } generate_syntax(Mode::Overwrite)?; generate_parser_tests(Mode::Overwrite)?; diff --git a/xtask/src/codegen/gen_lint_completions.rs b/xtask/src/codegen/gen_lint_completions.rs new file mode 100644 index 000000000..cffe954f8 --- /dev/null +++ b/xtask/src/codegen/gen_lint_completions.rs @@ -0,0 +1,113 @@ +//! Generates descriptors structure for unstable feature from Unstable Book +use std::path::{Path, PathBuf}; + +use quote::quote; +use walkdir::WalkDir; +use xshell::{cmd, read_file}; + +use crate::{ + codegen::{project_root, reformat, update, Mode, Result}, + run_rustfmt, +}; + +pub fn generate_lint_completions(mode: Mode) -> Result<()> { + if !Path::new("./target/rust").exists() { + cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; + } + + let ts_features = generate_descriptor("./target/rust/src/doc/unstable-book/src".into())?; + cmd!("curl http://rust-lang.github.io/rust-clippy/master/lints.json --output ./target/clippy_lints.json").run()?; + + let ts_clippy = generate_descriptor_clippy(&Path::new("./target/clippy_lints.json"))?; + let ts = quote! { + use crate::complete_attribute::LintCompletion; + #ts_features + #ts_clippy + }; + let contents = reformat(ts.to_string().as_str())?; + + let destination = project_root().join("crates/completion/src/generated_lint_completions.rs"); + update(destination.as_path(), &contents, mode)?; + run_rustfmt(mode)?; + + Ok(()) +} + +fn generate_descriptor(src_dir: PathBuf) -> Result { + let definitions = ["language-features", "library-features"] + .iter() + .flat_map(|it| WalkDir::new(src_dir.join(it))) + .filter_map(|e| e.ok()) + .filter(|entry| { + // Get all `.md ` files + entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md" + }) + .map(|entry| { + let path = entry.path(); + let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_"); + let doc = read_file(path).unwrap(); + + quote! { LintCompletion { label: #feature_ident, description: #doc } } + }); + + let ts = quote! { + pub(super) const FEATURES: &[LintCompletion] = &[ + #(#definitions),* + ]; + }; + + Ok(ts) +} + +#[derive(Default)] +struct ClippyLint { + help: String, + id: String, +} + +fn generate_descriptor_clippy(path: &Path) -> Result { + let file_content = read_file(path)?; + let mut clippy_lints: Vec = vec![]; + + for line in file_content.lines().map(|line| line.trim()) { + if line.starts_with(r#""id":"#) { + let clippy_lint = ClippyLint { + id: line + .strip_prefix(r#""id": ""#) + .expect("should be prefixed by id") + .strip_suffix(r#"","#) + .expect("should be suffixed by comma") + .into(), + help: String::new(), + }; + clippy_lints.push(clippy_lint) + } else if line.starts_with(r#""What it does":"#) { + // Typical line to strip: "What is doest": "Here is my useful content", + let prefix_to_strip = r#""What it does": ""#; + let suffix_to_strip = r#"","#; + + let clippy_lint = clippy_lints.last_mut().expect("clippy lint must already exist"); + clippy_lint.help = line + .strip_prefix(prefix_to_strip) + .expect("should be prefixed by what it does") + .strip_suffix(suffix_to_strip) + .expect("should be suffixed by comma") + .into(); + } + } + + let definitions = clippy_lints.into_iter().map(|clippy_lint| { + let lint_ident = format!("clippy::{}", clippy_lint.id); + let doc = clippy_lint.help; + + quote! { LintCompletion { label: #lint_ident, description: #doc } } + }); + + let ts = quote! { + pub(super) const CLIPPY_LINTS: &[LintCompletion] = &[ + #(#definitions),* + ]; + }; + + Ok(ts) +} diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index faaef2fd4..9de60c76c 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -131,6 +131,14 @@ https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after- } fn deny_clippy(path: &PathBuf, text: &String) { + let ignore = &[ + // The documentation in string literals may contain anything for its own purposes + "completion/src/generated_lint_completions.rs", + ]; + if ignore.iter().any(|p| path.ends_with(p)) { + return; + } + if text.contains("[\u{61}llow(clippy") { panic!( "\n\nallowing lints is forbidden: {}. @@ -214,7 +222,7 @@ fn check_todo(path: &Path, text: &str) { // `ast::make`. "ast/make.rs", // The documentation in string literals may contain anything for its own purposes - "completion/src/generated_features.rs", + "completion/src/generated_lint_completions.rs", ]; if need_todo.iter().any(|p| path.ends_with(p)) { return; -- cgit v1.2.3