]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/cargo_common_metadata.rs
Auto merge of #3701 - mikerite:fix-3118, r=phansch
[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_tool_lint, lint_array};
6 use syntax::{ast::*, source_map::DUMMY_SP};
7
8 use cargo_metadata;
9
10 /// **What it does:** Checks to see if all common metadata is defined in
11 /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
12 ///
13 /// **Why is this bad?** It will be more difficult for users to discover the
14 /// purpose of the crate, and key information related to it.
15 ///
16 /// **Known problems:** None.
17 ///
18 /// **Example:**
19 /// ```toml
20 /// # This `Cargo.toml` is missing an authors field:
21 /// [package]
22 /// name = "clippy"
23 /// version = "0.0.212"
24 /// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
25 /// repository = "https://github.com/rust-lang/rust-clippy"
26 /// readme = "README.md"
27 /// license = "MIT/Apache-2.0"
28 /// keywords = ["clippy", "lint", "plugin"]
29 /// categories = ["development-tools", "development-tools::cargo-plugins"]
30 /// ```
31 declare_clippy_lint! {
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     match value {
48         None => true,
49         Some(value) if value.is_empty() => true,
50         _ => false,
51     }
52 }
53
54 fn is_empty_vec(value: &[String]) -> bool {
55     // This works because empty iterators return true
56     value.iter().all(|v| v.is_empty())
57 }
58
59 pub struct Pass;
60
61 impl LintPass for Pass {
62     fn get_lints(&self) -> LintArray {
63         lint_array!(CARGO_COMMON_METADATA)
64     }
65 }
66
67 impl EarlyLintPass for Pass {
68     fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
69         let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
70             metadata
71         } else {
72             warning(cx, "could not read cargo metadata");
73             return;
74         };
75
76         for package in metadata.packages {
77             if is_empty_vec(&package.authors) {
78                 missing_warning(cx, &package, "package.authors");
79             }
80
81             if is_empty_str(&package.description) {
82                 missing_warning(cx, &package, "package.description");
83             }
84
85             if is_empty_str(&package.license) {
86                 missing_warning(cx, &package, "package.license");
87             }
88
89             if is_empty_str(&package.repository) {
90                 missing_warning(cx, &package, "package.repository");
91             }
92
93             if is_empty_str(&package.readme) {
94                 missing_warning(cx, &package, "package.readme");
95             }
96
97             if is_empty_vec(&package.keywords) {
98                 missing_warning(cx, &package, "package.keywords");
99             }
100
101             if is_empty_vec(&package.categories) {
102                 missing_warning(cx, &package, "package.categories");
103             }
104         }
105     }
106 }