]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/cargo_common_metadata.rs
Merge remote-tracking branch 'origin/beta_backport' into HEAD
[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     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(std::string::String::is_empty)
57 }
58
59 declare_lint_pass!(CargoCommonMetadata => [CARGO_COMMON_METADATA]);
60
61 impl EarlyLintPass for CargoCommonMetadata {
62     fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
63         let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
64             metadata
65         } else {
66             warning(cx, "could not read cargo metadata");
67             return;
68         };
69
70         for package in metadata.packages {
71             if is_empty_vec(&package.authors) {
72                 missing_warning(cx, &package, "package.authors");
73             }
74
75             if is_empty_str(&package.description) {
76                 missing_warning(cx, &package, "package.description");
77             }
78
79             if is_empty_str(&package.license) {
80                 missing_warning(cx, &package, "package.license");
81             }
82
83             if is_empty_str(&package.repository) {
84                 missing_warning(cx, &package, "package.repository");
85             }
86
87             if is_empty_str(&package.readme) {
88                 missing_warning(cx, &package, "package.readme");
89             }
90
91             if is_empty_vec(&package.keywords) {
92                 missing_warning(cx, &package, "package.keywords");
93             }
94
95             if is_empty_vec(&package.categories) {
96                 missing_warning(cx, &package, "package.categories");
97             }
98         }
99     }
100 }