Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add a basic linting system #13621

Merged
merged 2 commits into from
Mar 23, 2024
Merged

Conversation

Muscraft
Copy link
Member

This PR adds a basic linting system, the first step towards User control over cargo warnings. To demonstrate the system, a lint for #12826 is added. It supports controlling cargo lints via the [lints.cargo] table. Lints can be controlled either by a group or individually.

This is meant to lay down some fundamental infrastructure for the whole linting system and is not meant to be fully featured. Over time, features will be added that will help us reach a much more solid state.

@rustbot
Copy link
Collaborator

rustbot commented Mar 22, 2024

r? @weihanglo

rustbot has assigned @weihanglo.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-manifest Area: Cargo.toml issues A-workspaces Area: workspaces S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 22, 2024
Copy link
Member

@weihanglo weihanglo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 🎉 🎉

src/cargo/util/lints.rs Outdated Show resolved Hide resolved
Cargo.toml Show resolved Hide resolved
src/cargo/core/workspace.rs Show resolved Hide resolved
src/cargo/util/lints.rs Outdated Show resolved Hide resolved
tests/testsuite/features.rs Outdated Show resolved Hide resolved
src/cargo/util/lints.rs Outdated Show resolved Hide resolved
src/cargo/util/lints.rs Show resolved Hide resolved
tests/testsuite/lints.rs Outdated Show resolved Hide resolved
src/cargo/util/toml/mod.rs Outdated Show resolved Hide resolved
tests/testsuite/lints.rs Outdated Show resolved Hide resolved
}

impl Lint {
pub fn level(&self, lints: &TomlToolLints, edition: Edition) -> LintLevel {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing worth thinking hard is that how to avoid future build failure if people set something like -D cargo::all and we introduce new cargo lints.

A possible way is having every new lint capped max to warning in the current edition, and relax in the next.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way to potentially do this is to have each lint have a version field similar to Feature, that is the version they are stabilized in. From there, we add a "dynamic" group cargo:new-lints that all lints stabilized in that version are apart of. We then add documentation saying if a user doesn't want to break CI with -D cargo::all, they should have the command be:

cargo <cmd> -- -W cargo:new-lints -D cargo:all

I don't think this is perfect but it would work

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think rustc or clippy maintain compatibility with all

@bors
Copy link
Collaborator

bors commented Mar 22, 2024

☔ The latest upstream changes (presumably #13627) made this pull request unmergeable. Please resolve the merge conflicts.

});
// Add implicit features for optional dependencies if they weren't
// explicitly listed anywhere.
let explicitly_listed = user_defined_features.map_or(HashSet::new(), |f| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want some more test cases to exercising implicit features lint.

  • has dep:foo
  • has foo = [] feature
  • build.dependencies
  • target platform dependencies

Since this PR aims for the linting system. Let's leave it later.

src/cargo/util/lints.rs Show resolved Hide resolved
@weihanglo
Copy link
Member

As this is for linting system, the only blocker is an unstable flag for [lints.cargo]. Let me know if you are willing to work on it in this PR or later.

@rustbot rustbot added the A-unstable Area: nightly unstable support label Mar 23, 2024
Copy link
Member

@weihanglo weihanglo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Now looks pretty great and we're ready to merge this 👍🏾

@weihanglo
Copy link
Member

weihanglo commented Mar 23, 2024

This public interface of the linting system ([lints.cargo] table) is behind a new unstable flag -Z cargo-lints, with only a warning if not on nightly (non-blocking flag). Since the Cargo team is aware of the linting system Scott has been working on, I think we could merge it as-is without an FCP. Let me know if something goes wrong then.

@bors r+

@bors
Copy link
Collaborator

bors commented Mar 23, 2024

📌 Commit 307c7f8 has been approved by weihanglo

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 23, 2024
@bors
Copy link
Collaborator

bors commented Mar 23, 2024

⌛ Testing commit 307c7f8 with merge 61855e7...

@bors
Copy link
Collaborator

bors commented Mar 23, 2024

☀️ Test successful - checks-actions
Approved by: weihanglo
Pushing 61855e7 to master...

@bors bors merged commit 61855e7 into rust-lang:master Mar 23, 2024
23 checks passed
@Muscraft Muscraft deleted the linting-system branch March 23, 2024 19:30
@bors bors mentioned this pull request Mar 23, 2024
use std::path::Path;
use toml_edit::ImDocument;

fn get_span(document: &ImDocument<String>, path: &[&str], get_value: bool) -> Option<Range<usize>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: please put the focus of the file (what comes first) on the API and not the implementation details

use std::path::Path;
use toml_edit::ImDocument;

fn get_span(document: &ImDocument<String>, path: &[&str], get_value: bool) -> Option<Range<usize>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These needs documentation, especially around how arrays are handled


/// Gets the relative path to a manifest from the current working directory, or
/// the absolute path of the manifest if a relative path cannot be constructed
fn rel_cwd_manifest_path(path: &Path, gctx: &GlobalContext) -> String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, this belongs at the end of the file


/// Gets the relative path to a manifest from the current working directory, or
/// the absolute path of the manifest if a relative path cannot be constructed
fn rel_cwd_manifest_path(path: &Path, gctx: &GlobalContext) -> String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String in this is suspect. Should this have display in the name?

Or should we try to switch to camino?

Comment on lines +87 to +106
let level = self
.groups
.iter()
.map(|g| g.name)
.chain(std::iter::once(self.name))
.filter_map(|n| lints.get(n).map(|l| (n, l)))
.max_by_key(|(n, l)| (l.priority(), std::cmp::Reverse(*n)));

match level {
Some((_, toml_lint)) => toml_lint.level().into(),
None => {
if let Some((lint_edition, lint_level)) = self.edition_lint_opts {
if edition >= lint_edition {
return lint_level;
}
}
self.default_level
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we document anywhere the status of the warnings group?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: even if we don't support the group in [lints], we'd need to be able to support it in the future with #8424.

impl LintLevel {
pub fn to_diagnostic_level(self) -> Level {
match self {
LintLevel::Allow => Level::Note,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is Allow translating to Note. We shouldn't be showing Allow

{
continue;
}
if lint_level == LintLevel::Forbid || lint_level == LintLevel::Deny {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have an is_error function?

}
let level = lint_level.to_diagnostic_level();
let manifest_path = rel_cwd_manifest_path(path, gctx);
let message = level.title("unused optional dependency").snippet(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message doesn't make sense pre-2024

@@ -0,0 +1,34 @@
use cargo_test_support::prelude::*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of our other UI tests nest this deeply

name: "rust-2024-compatibility",
default_level: LintLevel::Allow,
desc: "warn about compatibility with Rust 2024",
edition_lint_opts: Some((Edition::Edition2024, LintLevel::Deny)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're only making this Deny for 2024? That means people can override this to allow it. If they do, we should make sure that the dependency truly is unused and doesn't create a feature.

}

impl Lint {
pub fn level(&self, lints: &TomlToolLints, edition: Edition) -> LintLevel {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should verify this handles Forbid correctly.

}
let level = lint_level.to_diagnostic_level();
let manifest_path = rel_cwd_manifest_path(path, gctx);
let message = level.title("unused optional dependency").snippet(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a transitional note that implicit features are no longer supported?

@epage epage mentioned this pull request Mar 25, 2024
6 tasks
bors added a commit that referenced this pull request Mar 25, 2024
refactor: Make lint names snake_case

When working on #13621, I somehow missed that lint names should be `snake_case` according to the [`rustc-dev-guide`](https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming) as well as [`RFC #344`](https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints).

This PR renames:
- `implicit-features` => `implicit_featires`
- `rust-2024-compatibility` => `rust_2024_compatibility`.

<hr>

Note: We should probably have some tooling to enforce this, but I was unsure if it belonged to this PR or another one. One solution would be to use a macro to create the `const LINT_NAME: Lint = {...}`, where `LINT_NAME` would be the `ident` as well as the `name: &'static str` and then have a method on `Lint` to make it lowercase as needed. This is what `rustc` does, and it could work well here. It would ensure snake case as `const` names need to be [`SCREAMING_SNAKE_CASE`](https://rust-lang.github.io/rfcs/0430-finalizing-naming-conventions.html#general-naming-conventions), or a warning is shown.
}
let level = lint_level.to_diagnostic_level();
let manifest_path = rel_cwd_manifest_path(path, gctx);
let message = level.title("unused optional dependency").snippet(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rustc/clippy lints display the lint name (and where the level was set) on first instance of it being reported

Comment on lines +1100 to +1103
let path = path.join("Cargo.toml");
if let MaybePackage::Package(pkg) = maybe_pkg {
self.emit_lints(pkg, &path)?
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a test to ensure this is subject to cap-lints

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 25, 2024
Update cargo

13 commits in d438c80c45c24be676ef5867edc79d0a14910efe..a510712d05c6c98f987af24dd73cdfafee8922e6
2024-03-19 16:11:22 +0000 to 2024-03-25 03:45:54 +0000
- Remove unnecessary test (rust-lang/cargo#13637)
- Use `gitoxide` for `list_files_git` (rust-lang/cargo#13592)
- fix: Warn on -Zlints (rust-lang/cargo#13632)
- feat: Add a basic linting system (rust-lang/cargo#13621)
- docs: remove untrue TODO for `native_dirs` (rust-lang/cargo#13631)
- refactor(testsuite): Rename lints to lints_table (rust-lang/cargo#13627)
- Fix debuginfo strip when using `--target` (rust-lang/cargo#13618)
- refactor(toml): Push diagnostic complexity on annotate-snippets (rust-lang/cargo#13619)
- Fix publish script due to crates.io CDN change (rust-lang/cargo#13614)
- fix(alias): dont panic when resolving an empty alias (rust-lang/cargo#13613)
- Update annotate snippets (rust-lang/cargo#13609)
- refactor(vendor): tiny not important refactors (rust-lang/cargo#13610)
- feat: Report some dependency changes on any command (rust-lang/cargo#13561)

r? ghost
Comment on lines +1466 to +1468
if tool == "cargo" && !gctx.cli_unstable().cargo_lints {
warn_for_cargo_lint_feature(gctx, warnings);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to strip lints.cargo before we publish?

@rustbot rustbot added this to the 1.79.0 milestone Mar 25, 2024
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Mar 26, 2024
Update cargo

13 commits in d438c80c45c24be676ef5867edc79d0a14910efe..a510712d05c6c98f987af24dd73cdfafee8922e6
2024-03-19 16:11:22 +0000 to 2024-03-25 03:45:54 +0000
- Remove unnecessary test (rust-lang/cargo#13637)
- Use `gitoxide` for `list_files_git` (rust-lang/cargo#13592)
- fix: Warn on -Zlints (rust-lang/cargo#13632)
- feat: Add a basic linting system (rust-lang/cargo#13621)
- docs: remove untrue TODO for `native_dirs` (rust-lang/cargo#13631)
- refactor(testsuite): Rename lints to lints_table (rust-lang/cargo#13627)
- Fix debuginfo strip when using `--target` (rust-lang/cargo#13618)
- refactor(toml): Push diagnostic complexity on annotate-snippets (rust-lang/cargo#13619)
- Fix publish script due to crates.io CDN change (rust-lang/cargo#13614)
- fix(alias): dont panic when resolving an empty alias (rust-lang/cargo#13613)
- Update annotate snippets (rust-lang/cargo#13609)
- refactor(vendor): tiny not important refactors (rust-lang/cargo#13610)
- feat: Report some dependency changes on any command (rust-lang/cargo#13561)

r? ghost
@ehuss
Copy link
Contributor

ehuss commented Mar 26, 2024

Would it be possible to update unstable.md with documentation about this feature?

Also, is #12235 the main tracking issue for what needs to be done next? Is there an outline of what steps need to be taken?

bors added a commit that referenced this pull request Apr 19, 2024
Unused dependencies cleanup

The implementation of #12826 was used as a demonstration for #12235, in #13621. The demonstration implementation was far from ideal and was lacking a few features. This PR makes the implementation "feature complete", and fixes known bugs with the initial implementation.
bors added a commit that referenced this pull request Apr 29, 2024
test(cargo-lints): Add a test to ensure cap-lints works

When implementing the linting system, [it was noted that there was no test to ensure this it is to cap-lints](#13621 (comment)), this PR adds that missing test.
bors added a commit that referenced this pull request May 9, 2024
Refactor cargo lint tests

In #13621, it was brought up that [the lints tests are nested more deeply than other UI tests](#13621 (comment)). This got me wondering if there was a better way to structure all the lint tests.
What I came up with was:
- Lints should not have UI tests, only parts of the diagnostic system, i.e., how warnings, errors, notes, etc., look
  - This is because UI tests should focus on parts of the system that make up each lint's output
  - We can always add UI tests for each lint if desired
- All tests related to the linting system should live in `tests/testsuite/lints/`
- Tests related to `[lints.cargo]` should stay in `lints_table.rs` as it is for the whole lints table
- Each lint will get a file in `lints/` for all of its tests
  - This makes `lints/mod.rs` smaller and targeted only at tests for the linting system itself
  - It makes it much easier to know where to place a test
bors added a commit that referenced this pull request May 9, 2024
Refactor cargo lint tests

In #13621, it was brought up that [the lints tests are nested more deeply than other UI tests](#13621 (comment)). This got me wondering if there was a better way to structure all the lint tests.
What I came up with was:
- Lints should not have UI tests, only parts of the diagnostic system, i.e., how warnings, errors, notes, etc., look
  - This is because UI tests should focus on parts of the system that make up each lint's output
  - We can always add UI tests for each lint if desired
- All tests related to the linting system should live in `tests/testsuite/lints/`
- Tests related to `[lints.cargo]` should stay in `lints_table.rs` as it is for the whole lints table
- Each lint will get a file in `lints/` for all of its tests
  - This makes `lints/mod.rs` smaller and targeted only at tests for the linting system itself
  - It makes it much easier to know where to place a test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-manifest Area: Cargo.toml issues A-unstable Area: nightly unstable support A-workspaces Area: workspaces S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants