]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/cargo_common_metadata.rs
Auto merge of #4551 - mikerite:fix-ice-reporting, r=llogiq
[rust.git] / clippy_lints / src / cargo_common_metadata.rs
1 //! lint on missing cargo common metadata
2
3 use crate::utils::span_lint;
4 use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
5 use rustc::{declare_lint_pass, declare_tool_lint};
6 use syntax::{ast::*, source_map::DUMMY_SP};
7
8 use cargo_metadata;
9
10 declare_clippy_lint! {
11     /// **What it does:** Checks to see if all common metadata is defined in
12     /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
13     ///
14     /// **Why is this bad?** It will be more difficult for users to discover the
15     /// purpose of the crate, and key information related to it.
16     ///
17     /// **Known problems:** None.
18     ///
19     /// **Example:**
20     /// ```toml
21     /// # This `Cargo.toml` is missing an authors field:
22     /// [package]
23     /// name = "clippy"
24     /// version = "0.0.212"
25     /// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
26     /// repository = "https://github.com/rust-lang/rust-clippy"
27     /// readme = "README.md"
28     /// license = "MIT/Apache-2.0"
29     /// keywords = ["clippy", "lint", "plugin"]
30     /// categories = ["development-tools", "development-tools::cargo-plugins"]
31     /// ```
32     pub CARGO_COMMON_METADATA,
33     cargo,
34     "common metadata is defined in `Cargo.toml`"
35 }
36
37 fn warning(cx: &EarlyContext<'_>, message: &str) {
38     span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message);
39 }
40
41 fn missing_warning(cx: &EarlyContext<'_>, package: &cargo_metadata::Package, field: &str) {
42     let message = format!("package `{}` is missing `{}` metadata", package.name, field);
43     warning(cx, &message);
44 }
45
46 fn is_empty_str(value: &Option<String>) -> bool {
47     value.as_ref().map_or(true, String::is_empty)
48 }
49
50 fn is_empty_vec(value: &[String]) -> bool {
51     // This works because empty iterators return true
52     value.iter().all(std::string::String::is_empty)
53 }
54
55 declare_lint_pass!(CargoCommonMetadata => [CARGO_COMMON_METADATA]);
56
57 impl EarlyLintPass for CargoCommonMetadata {
58     fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
59         let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
60             metadata
61         } else {
62             warning(cx, "could not read cargo metadata");
63             return;
64         };
65
66         for package in metadata.packages {
67             if is_empty_vec(&package.authors) {
68                 missing_warning(cx, &package, "package.authors");
69             }
70
71             if is_empty_str(&package.description) {
72                 missing_warning(cx, &package, "package.description");
73             }
74
75             if is_empty_str(&package.license) {
76                 missing_warning(cx, &package, "package.license");
77             }
78
79             if is_empty_str(&package.repository) {
80                 missing_warning(cx, &package, "package.repository");
81             }
82
83             if is_empty_str(&package.readme) {
84                 missing_warning(cx, &package, "package.readme");
85             }
86
87             if is_empty_vec(&package.keywords) {
88                 missing_warning(cx, &package, "package.keywords");
89             }
90
91             if is_empty_vec(&package.categories) {
92                 missing_warning(cx, &package, "package.categories");
93             }
94         }
95     }
96 }