Skip to main content

cargo/diagnostics/rules/
min_opt_level_hint.rs

1use std::path::Path;
2
3use cargo_util_terminal::report::AnnotationKind;
4use cargo_util_terminal::report::Group;
5use cargo_util_terminal::report::Level;
6use cargo_util_terminal::report::Origin;
7use cargo_util_terminal::report::Snippet;
8
9use crate::CargoResult;
10use crate::GlobalContext;
11use crate::diagnostics::ScopedDiagnosticStats;
12use crate::diagnostics::TomlSpan;
13use crate::diagnostics::get_key_value_span;
14use crate::diagnostics::workspace_rel_path;
15use crate::workspace::Package;
16use crate::workspace::Workspace;
17use crate::workspace::profiles::{MinOptLevelHintError, parse_min_opt_level_hint};
18
19/// Emits diagnostics for `hints.min-opt-level`.
20#[tracing::instrument(skip_all)]
21pub(crate) fn diagnose_package(
22    ws: &Workspace<'_>,
23    pkg: &Package,
24    path: &Path,
25    pkg_stats: &mut ScopedDiagnosticStats<'_>,
26    gctx: &GlobalContext,
27) -> CargoResult<()> {
28    let manifest_path = workspace_rel_path(ws, path);
29    let min_opt_level = match parse_min_opt_level_hint(
30        pkg.hints().and_then(|hints| hints.min_opt_level.as_ref()),
31    ) {
32        Ok(level) => level,
33        Err(err) => {
34            let (title, label) = match err {
35                MinOptLevelHintError::OutOfRange { level, expected } => (
36                    format!("ignoring unsupported value `{level}` for `hints.min-opt-level`"),
37                    format!(
38                        "expected an integer from {} to {}",
39                        expected.start(),
40                        expected.end()
41                    ),
42                ),
43                MinOptLevelHintError::WrongType(value_type) => (
44                    format!(
45                        "ignoring unsupported value type `{value_type}` for `hints.min-opt-level`"
46                    ),
47                    "expected an integer".to_owned(),
48                ),
49            };
50            let group = Group::with_title(Level::WARNING.primary_title(title));
51            let group = match hint_span(pkg) {
52                Some((contents, span)) => group.element(
53                    Snippet::source(contents)
54                        .path(&manifest_path)
55                        .annotation(AnnotationKind::Primary.span(span.value).label(label)),
56                ),
57                None => group.element(Origin::path(&manifest_path)),
58            };
59            pkg_stats.record_warning();
60            gctx.shell().print_report(&[group], false)?;
61            None
62        }
63    };
64
65    if matches!(min_opt_level, Some(1..)) && !gctx.cli_unstable().hint_min_opt_level {
66        let group =
67            Group::with_title(Level::WARNING.primary_title("ignoring `hints.min-opt-level`"));
68        let group = match hint_span(pkg) {
69            Some((contents, span)) => group.element(
70                Snippet::source(contents)
71                    .path(&manifest_path)
72                    .annotation(AnnotationKind::Primary.span(span.key.start..span.value.end)),
73            ),
74            None => group.element(Origin::path(&manifest_path)),
75        };
76        let group = group.element(Level::HELP.message("pass `-Zhint-min-opt-level` to enable it"));
77        pkg_stats.record_warning();
78        gctx.shell().print_report(&[group], false)?;
79    }
80
81    Ok(())
82}
83
84/// Locates `hints.min-opt-level` in the package's original manifest, if its source is available.
85fn hint_span(pkg: &Package) -> Option<(&str, TomlSpan)> {
86    let manifest = pkg.manifest();
87    let contents = manifest.contents()?;
88    let document = manifest.document()?;
89    let span = get_key_value_span(document, &["hints", "min-opt-level"])?;
90    Some((contents, span))
91}