mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
feat: Add lint for global use of hint-mostly-unused
This commit is contained in:
parent
d826a65fef
commit
da5d9152fa
@ -25,7 +25,9 @@ use crate::util::context::FeatureUnification;
|
|||||||
use crate::util::edit_distance;
|
use crate::util::edit_distance;
|
||||||
use crate::util::errors::{CargoResult, ManifestError};
|
use crate::util::errors::{CargoResult, ManifestError};
|
||||||
use crate::util::interning::InternedString;
|
use crate::util::interning::InternedString;
|
||||||
use crate::util::lints::{analyze_cargo_lints_table, check_im_a_teapot};
|
use crate::util::lints::{
|
||||||
|
analyze_cargo_lints_table, blanket_hint_mostly_unused, check_im_a_teapot,
|
||||||
|
};
|
||||||
use crate::util::toml::{InheritableFields, read_manifest};
|
use crate::util::toml::{InheritableFields, read_manifest};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
Filesystem, GlobalContext, IntoUrl, context::CargoResolverConfig, context::ConfigRelativePath,
|
Filesystem, GlobalContext, IntoUrl, context::CargoResolverConfig, context::ConfigRelativePath,
|
||||||
@ -1287,9 +1289,9 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_ws_lints(&self) -> CargoResult<()> {
|
pub fn emit_ws_lints(&self) -> CargoResult<()> {
|
||||||
let error_count = 0;
|
let mut error_count = 0;
|
||||||
|
|
||||||
let _cargo_lints = match self.root_maybe() {
|
let cargo_lints = match self.root_maybe() {
|
||||||
MaybePackage::Package(pkg) => {
|
MaybePackage::Package(pkg) => {
|
||||||
let toml = pkg.manifest().normalized_toml();
|
let toml = pkg.manifest().normalized_toml();
|
||||||
if let Some(ws) = &toml.workspace {
|
if let Some(ws) = &toml.workspace {
|
||||||
@ -1314,6 +1316,19 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
// Calls to lint functions go in here
|
// Calls to lint functions go in here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a short term hack to allow `blanket_hint_mostly_unused`
|
||||||
|
// to run without requiring `-Zcargo-lints`, which should hopefully
|
||||||
|
// improve the testing expierience while we are collecting feedback
|
||||||
|
if self.gctx.cli_unstable().profile_hint_mostly_unused {
|
||||||
|
blanket_hint_mostly_unused(
|
||||||
|
self.root_maybe(),
|
||||||
|
self.root_manifest(),
|
||||||
|
&cargo_lints,
|
||||||
|
&mut error_count,
|
||||||
|
self.gctx,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
if error_count > 0 {
|
if error_count > 0 {
|
||||||
Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
|
Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
|
||||||
"encountered {error_count} errors(s) while running lints"
|
"encountered {error_count} errors(s) while running lints"
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use crate::core::{Edition, Feature, Features, Manifest, Package};
|
use crate::core::{Edition, Feature, Features, Manifest, MaybePackage, Package};
|
||||||
use crate::{CargoResult, GlobalContext};
|
use crate::{CargoResult, GlobalContext};
|
||||||
use annotate_snippets::{AnnotationKind, Group, Level, Snippet};
|
use annotate_snippets::{AnnotationKind, Group, Level, Patch, Snippet};
|
||||||
use cargo_util_schemas::manifest::{TomlLintLevel, TomlToolLints};
|
use cargo_util_schemas::manifest::{ProfilePackageSpec, TomlLintLevel, TomlToolLints};
|
||||||
use pathdiff::diff_paths;
|
use pathdiff::diff_paths;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
const LINT_GROUPS: &[LintGroup] = &[TEST_DUMMY_UNSTABLE];
|
const LINT_GROUPS: &[LintGroup] = &[TEST_DUMMY_UNSTABLE];
|
||||||
pub const LINTS: &[Lint] = &[IM_A_TEAPOT, UNKNOWN_LINTS];
|
pub const LINTS: &[Lint] = &[BLANKET_HINT_MOSTLY_UNUSED, IM_A_TEAPOT, UNKNOWN_LINTS];
|
||||||
|
|
||||||
pub fn analyze_cargo_lints_table(
|
pub fn analyze_cargo_lints_table(
|
||||||
pkg: &Package,
|
pkg: &Package,
|
||||||
@ -473,6 +473,158 @@ pub fn check_im_a_teapot(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BLANKET_HINT_MOSTLY_UNUSED: Lint = Lint {
|
||||||
|
name: "blanket_hint_mostly_unused",
|
||||||
|
desc: "blanket_hint_mostly_unused lint",
|
||||||
|
groups: &[],
|
||||||
|
default_level: LintLevel::Warn,
|
||||||
|
edition_lint_opts: None,
|
||||||
|
feature_gate: None,
|
||||||
|
docs: Some(
|
||||||
|
r#"
|
||||||
|
### What it does
|
||||||
|
Checks if `hint-mostly-unused` being applied to all dependencies.
|
||||||
|
|
||||||
|
### Why it is bad
|
||||||
|
`hint-mostly-unused` indicates that most of a crate's API surface will go
|
||||||
|
unused by anything depending on it; this hint can speed up the build by
|
||||||
|
attempting to minimize compilation time for items that aren't used at all.
|
||||||
|
Misapplication to crates that don't fit that criteria will slow down the build
|
||||||
|
rather than speeding it up. It should be selectively applied to dependencies
|
||||||
|
that meet these criteria. Applying it globally is always a misapplication and
|
||||||
|
will likely slow down the build.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```toml
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
hint-mostly-unused = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Should instead be:
|
||||||
|
```toml
|
||||||
|
[profile.dev.package.huge-mostly-unused-dependency]
|
||||||
|
hint-mostly-unused = true
|
||||||
|
```
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn blanket_hint_mostly_unused(
|
||||||
|
maybe_pkg: &MaybePackage,
|
||||||
|
path: &Path,
|
||||||
|
pkg_lints: &TomlToolLints,
|
||||||
|
error_count: &mut usize,
|
||||||
|
gctx: &GlobalContext,
|
||||||
|
) -> CargoResult<()> {
|
||||||
|
let (lint_level, reason) = BLANKET_HINT_MOSTLY_UNUSED.level(
|
||||||
|
pkg_lints,
|
||||||
|
maybe_pkg.edition(),
|
||||||
|
maybe_pkg.unstable_features(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if lint_level == LintLevel::Allow {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let level = lint_level.to_diagnostic_level();
|
||||||
|
let manifest_path = rel_cwd_manifest_path(path, gctx);
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
|
||||||
|
if let Some(profiles) = maybe_pkg.profiles() {
|
||||||
|
for (profile_name, top_level_profile) in &profiles.0 {
|
||||||
|
if let Some(true) = top_level_profile.hint_mostly_unused {
|
||||||
|
paths.push((
|
||||||
|
vec!["profile", profile_name.as_str(), "hint-mostly-unused"],
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(build_override) = &top_level_profile.build_override
|
||||||
|
&& let Some(true) = build_override.hint_mostly_unused
|
||||||
|
{
|
||||||
|
paths.push((
|
||||||
|
vec![
|
||||||
|
"profile",
|
||||||
|
profile_name.as_str(),
|
||||||
|
"build-override",
|
||||||
|
"hint-mostly-unused",
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(packages) = &top_level_profile.package
|
||||||
|
&& let Some(profile) = packages.get(&ProfilePackageSpec::All)
|
||||||
|
&& let Some(true) = profile.hint_mostly_unused
|
||||||
|
{
|
||||||
|
paths.push((
|
||||||
|
vec![
|
||||||
|
"profile",
|
||||||
|
profile_name.as_str(),
|
||||||
|
"package",
|
||||||
|
"*",
|
||||||
|
"hint-mostly-unused",
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, (path, show_per_pkg_suggestion)) in paths.iter().enumerate() {
|
||||||
|
if lint_level.is_error() {
|
||||||
|
*error_count += 1;
|
||||||
|
}
|
||||||
|
let title = "`hint-mostly-unused` is being blanket applied to all dependencies";
|
||||||
|
let help_txt =
|
||||||
|
"scope `hint-mostly-unused` to specific packages with a lot of unused object code";
|
||||||
|
if let (Some(span), Some(table_span)) = (
|
||||||
|
get_key_value_span(maybe_pkg.document(), &path),
|
||||||
|
get_key_value_span(maybe_pkg.document(), &path[..path.len() - 1]),
|
||||||
|
) {
|
||||||
|
let mut report = Vec::new();
|
||||||
|
let mut primary_group = level.clone().primary_title(title).element(
|
||||||
|
Snippet::source(maybe_pkg.contents())
|
||||||
|
.path(&manifest_path)
|
||||||
|
.annotation(
|
||||||
|
AnnotationKind::Primary.span(table_span.key.start..table_span.key.end),
|
||||||
|
)
|
||||||
|
.annotation(AnnotationKind::Context.span(span.key.start..span.value.end)),
|
||||||
|
);
|
||||||
|
|
||||||
|
if *show_per_pkg_suggestion {
|
||||||
|
report.push(
|
||||||
|
Level::HELP.secondary_title(help_txt).element(
|
||||||
|
Snippet::source(maybe_pkg.contents())
|
||||||
|
.path(&manifest_path)
|
||||||
|
.patch(Patch::new(
|
||||||
|
table_span.key.end..table_span.key.end,
|
||||||
|
".package.<pkg_name>",
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
primary_group = primary_group.element(Level::HELP.message(help_txt));
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
primary_group =
|
||||||
|
primary_group
|
||||||
|
.element(Level::NOTE.message(
|
||||||
|
BLANKET_HINT_MOSTLY_UNUSED.emitted_source(lint_level, reason),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The primary group should always be first
|
||||||
|
report.insert(0, primary_group);
|
||||||
|
|
||||||
|
gctx.shell().print_report(&report, lint_level.force())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
const UNKNOWN_LINTS: Lint = Lint {
|
const UNKNOWN_LINTS: Lint = Lint {
|
||||||
name: "unknown_lints",
|
name: "unknown_lints",
|
||||||
desc: "unknown lint",
|
desc: "unknown lint",
|
||||||
|
@ -5,8 +5,37 @@ Note: [Cargo's linting system is unstable](unstable.md#lintscargo) and can only
|
|||||||
## Warn-by-default
|
## Warn-by-default
|
||||||
|
|
||||||
These lints are all set to the 'warn' level by default.
|
These lints are all set to the 'warn' level by default.
|
||||||
|
- [`blanket_hint_mostly_unused`](#blanket_hint_mostly_unused)
|
||||||
- [`unknown_lints`](#unknown_lints)
|
- [`unknown_lints`](#unknown_lints)
|
||||||
|
|
||||||
|
## `blanket_hint_mostly_unused`
|
||||||
|
Set to `warn` by default
|
||||||
|
|
||||||
|
### What it does
|
||||||
|
Checks if `hint-mostly-unused` being applied to all dependencies.
|
||||||
|
|
||||||
|
### Why it is bad
|
||||||
|
`hint-mostly-unused` indicates that most of a crate's API surface will go
|
||||||
|
unused by anything depending on it; this hint can speed up the build by
|
||||||
|
attempting to minimize compilation time for items that aren't used at all.
|
||||||
|
Misapplication to crates that don't fit that criteria will slow down the build
|
||||||
|
rather than speeding it up. It should be selectively applied to dependencies
|
||||||
|
that meet these criteria. Applying it globally is always a misapplication and
|
||||||
|
will likely slow down the build.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```toml
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
hint-mostly-unused = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Should instead be:
|
||||||
|
```toml
|
||||||
|
[profile.dev.package.huge-mostly-unused-dependency]
|
||||||
|
hint-mostly-unused = true
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## `unknown_lints`
|
## `unknown_lints`
|
||||||
Set to `warn` by default
|
Set to `warn` by default
|
||||||
|
|
||||||
|
@ -22,6 +22,19 @@ hint-mostly-unused = true
|
|||||||
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
||||||
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[WARNING] `hint-mostly-unused` is being blanket applied to all dependencies
|
||||||
|
--> Cargo.toml:7:10
|
||||||
|
|
|
||||||
|
7 | [profile.dev]
|
||||||
|
| ^^^
|
||||||
|
8 | hint-mostly-unused = true
|
||||||
|
| -------------------------
|
||||||
|
|
|
||||||
|
= [NOTE] `cargo::blanket_hint_mostly_unused` is set to `warn` by default
|
||||||
|
[HELP] scope `hint-mostly-unused` to specific packages with a lot of unused object code
|
||||||
|
|
|
||||||
|
7 | [profile.dev.package.<pkg_name>]
|
||||||
|
| +++++++++++++++++++
|
||||||
[CHECKING] foo v0.0.1 ([ROOT]/foo)
|
[CHECKING] foo v0.0.1 ([ROOT]/foo)
|
||||||
[RUNNING] `rustc --crate-name foo [..]`
|
[RUNNING] `rustc --crate-name foo [..]`
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||||
@ -50,6 +63,16 @@ hint-mostly-unused = true
|
|||||||
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
||||||
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[WARNING] `hint-mostly-unused` is being blanket applied to all dependencies
|
||||||
|
--> Cargo.toml:7:22
|
||||||
|
|
|
||||||
|
7 | [profile.dev.package."*"]
|
||||||
|
| ^^^
|
||||||
|
8 | hint-mostly-unused = true
|
||||||
|
| -------------------------
|
||||||
|
|
|
||||||
|
= [HELP] scope `hint-mostly-unused` to specific packages with a lot of unused object code
|
||||||
|
= [NOTE] `cargo::blanket_hint_mostly_unused` is set to `warn` by default
|
||||||
[CHECKING] foo v0.0.1 ([ROOT]/foo)
|
[CHECKING] foo v0.0.1 ([ROOT]/foo)
|
||||||
[RUNNING] `rustc --crate-name foo [..]`
|
[RUNNING] `rustc --crate-name foo [..]`
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||||
@ -78,6 +101,16 @@ hint-mostly-unused = true
|
|||||||
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
||||||
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[WARNING] `hint-mostly-unused` is being blanket applied to all dependencies
|
||||||
|
--> Cargo.toml:7:14
|
||||||
|
|
|
||||||
|
7 | [profile.dev.build-override]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
8 | hint-mostly-unused = true
|
||||||
|
| -------------------------
|
||||||
|
|
|
||||||
|
= [HELP] scope `hint-mostly-unused` to specific packages with a lot of unused object code
|
||||||
|
= [NOTE] `cargo::blanket_hint_mostly_unused` is set to `warn` by default
|
||||||
[CHECKING] foo v0.0.1 ([ROOT]/foo)
|
[CHECKING] foo v0.0.1 ([ROOT]/foo)
|
||||||
[RUNNING] `rustc --crate-name foo [..]`
|
[RUNNING] `rustc --crate-name foo [..]`
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||||
@ -115,6 +148,16 @@ authors = []
|
|||||||
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
p.cargo("check -Zprofile-hint-mostly-unused -v")
|
||||||
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
.masquerade_as_nightly_cargo(&["profile-hint-mostly-unused", "cargo-lints"])
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
|
[WARNING] `hint-mostly-unused` is being blanket applied to all dependencies
|
||||||
|
--> Cargo.toml:5:22
|
||||||
|
|
|
||||||
|
5 | [profile.dev.package."*"]
|
||||||
|
| ^^^
|
||||||
|
6 | hint-mostly-unused = true
|
||||||
|
| -------------------------
|
||||||
|
|
|
||||||
|
= [HELP] scope `hint-mostly-unused` to specific packages with a lot of unused object code
|
||||||
|
= [NOTE] `cargo::blanket_hint_mostly_unused` is set to `warn` by default
|
||||||
[CHECKING] foo v0.0.1 ([ROOT]/foo/foo)
|
[CHECKING] foo v0.0.1 ([ROOT]/foo/foo)
|
||||||
[RUNNING] `rustc --crate-name foo [..]`
|
[RUNNING] `rustc --crate-name foo [..]`
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||||
|
Loading…
x
Reference in New Issue
Block a user