#
cargo/lints/rules/
blanket_hint_mostly_unused.rs

    1use std::path::Path;
    2
    3use annotate_snippets::AnnotationKind;
    4use annotate_snippets::Group;
    5use annotate_snippets::Level;
    6use annotate_snippets::Origin;
    7use annotate_snippets::Patch;
    8use annotate_snippets::Snippet;
    9use cargo_util_schemas::manifest::ProfilePackageSpec;
    10use cargo_util_schemas::manifest::TomlToolLints;
    11
    12use crate::CargoResult;
    13use crate::GlobalContext;
    14use crate::core::MaybePackage;
    15use crate::lints::Lint;
    16use crate::lints::LintLevel;
    17use crate::lints::SUSPICIOUS;
    18use crate::lints::get_key_value_span;
    19use crate::lints::rel_cwd_manifest_path;
    20
    21pub const LINT: Lint = Lint {
    22    name: "blanket_hint_mostly_unused",
    23    desc: "blanket_hint_mostly_unused lint",
    24    primary_group: &SUSPICIOUS,
    25    edition_lint_opts: None,
    26    feature_gate: None,
    27    docs: Some(
    28        r#"
    29### What it does
    30Checks if `hint-mostly-unused` being applied to all dependencies.
    31
    32### Why it is bad
    33`hint-mostly-unused` indicates that most of a crate's API surface will go
    34unused by anything depending on it; this hint can speed up the build by
    35attempting to minimize compilation time for items that aren't used at all.
    36Misapplication to crates that don't fit that criteria will slow down the build
    37rather than speeding it up. It should be selectively applied to dependencies
    38that meet these criteria. Applying it globally is always a misapplication and
    39will likely slow down the build.
    40
    41### Example
    42```toml
    43[profile.dev.package."*"]
    44hint-mostly-unused = true
    45```
    46
    47Should instead be:
    48```toml
    49[profile.dev.package.huge-mostly-unused-dependency]
    50hint-mostly-unused = true
    51```
    52"#,
    53    ),
    54};
    55
    56pub fn blanket_hint_mostly_unused(
    57    maybe_pkg: &MaybePackage,
    58    path: &Path,
    59    pkg_lints: &TomlToolLints,
    60    error_count: &mut usize,
    61    gctx: &GlobalContext,
    62) -> CargoResult<()> {
    63    let (lint_level, reason) = LINT.level(
    64        pkg_lints,
    65        maybe_pkg.edition(),
    66        maybe_pkg.unstable_features(),
    67    );
    68
    69    if lint_level == LintLevel::Allow {
    70        return Ok(());
    71    }
    72
    73    let level = lint_level.to_diagnostic_level();
    74    let manifest_path = rel_cwd_manifest_path(path, gctx);
    75    let mut paths = Vec::new();
    76
    77    if let Some(profiles) = maybe_pkg.profiles() {
    78        for (profile_name, top_level_profile) in &profiles.0 {
    79            if let Some(true) = top_level_profile.hint_mostly_unused {
    80                paths.push((
    81                    vec!["profile", profile_name.as_str(), "hint-mostly-unused"],
    82                    true,
    83                ));
    84            }
    85
    86            if let Some(build_override) = &top_level_profile.build_override
    87                && let Some(true) = build_override.hint_mostly_unused
    88            {
    89                paths.push((
    90                    vec![
    91                        "profile",
    92                        profile_name.as_str(),
    93                        "build-override",
    94                        "hint-mostly-unused",
    95                    ],
    96                    false,
    97                ));
    98            }
    99
    100            if let Some(packages) = &top_level_profile.package
    101                && let Some(profile) = packages.get(&ProfilePackageSpec::All)
    102                && let Some(true) = profile.hint_mostly_unused
    103            {
    104                paths.push((
    105                    vec![
    106                        "profile",
    107                        profile_name.as_str(),
    108                        "package",
    109                        "*",
    110                        "hint-mostly-unused",
    111                    ],
    112                    false,
    113                ));
    114            }
    115        }
    116    }
    117
    118    for (i, (path, show_per_pkg_suggestion)) in paths.iter().enumerate() {
    119        if lint_level.is_error() {
    120            *error_count += 1;
    121        }
    122        let title = "`hint-mostly-unused` is being blanket applied to all dependencies";
    123        let help_txt =
    124            "scope `hint-mostly-unused` to specific packages with a lot of unused object code";
    125
    126        let mut report = Vec::new();
    127        let mut primary_group = Group::with_title(level.clone().primary_title(title));
    128
    129        if let Some(contents) = maybe_pkg.contents()
    130            && let Some(document) = maybe_pkg.document()
    131            && let Some(span) = get_key_value_span(document, &path)
    132            && let Some(table_span) = get_key_value_span(document, &path[..path.len() - 1])
    133        {
    134            primary_group = primary_group.element(
    135                Snippet::source(contents)
    136                    .path(&manifest_path)
    137                    .annotation(
    138                        AnnotationKind::Primary.span(table_span.key.start..table_span.key.end),
    139                    )
    140                    .annotation(AnnotationKind::Context.span(span.key.start..span.value.end)),
    141            );
    142        } else {
    143            primary_group = primary_group.element(Origin::path(&manifest_path))
    144        }
    145
    146        if *show_per_pkg_suggestion {
    147            let help_group = Group::with_title(Level::HELP.secondary_title(help_txt));
    148
    149            report.push(
    150                if let Some(contents) = maybe_pkg.contents()
    151                    && let Some(document) = maybe_pkg.document()
    152                    && let Some(table_span) = get_key_value_span(document, &path[..path.len() - 1])
    153                {
    154                    help_group.element(Snippet::source(contents).path(&manifest_path).patch(
    155                        Patch::new(
    156                            table_span.key.end..table_span.key.end,
    157                            ".package.<pkg_name>",
    158                        ),
    159                    ))
    160                } else {
    161                    help_group.element(Origin::path(&manifest_path))
    162                },
    163            );
    164        } else {
    165            primary_group = primary_group.element(Level::HELP.message(help_txt));
    166        }
    167
    168        if i == 0 {
    169            primary_group =
    170                primary_group.element(Level::NOTE.message(LINT.emitted_source(lint_level, reason)));
    171        }
    172
    173        // The primary group should always be first
    174        report.insert(0, primary_group);
    175
    176        gctx.shell().print_report(&report, lint_level.force())?;
    177    }
    178
    179    Ok(())
    180}