-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Conversation
r? @weihanglo rustbot has assigned @weihanglo. Use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 🎉 🎉
} | ||
|
||
impl Lint { | ||
pub fn level(&self, lints: &TomlToolLints, edition: Edition) -> LintLevel { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
☔ The latest upstream changes (presumably #13627) made this pull request unmergeable. Please resolve the merge conflicts. |
998524f
to
df44151
Compare
}); | ||
// Add implicit features for optional dependencies if they weren't | ||
// explicitly listed anywhere. | ||
let explicitly_listed = user_defined_features.map_or(HashSet::new(), |f| { |
There was a problem hiding this comment.
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.
As this is for linting system, the only blocker is an unstable flag for |
df44151
to
307c7f8
Compare
There was a problem hiding this 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 👍🏾
This public interface of the linting system ( @bors r+ |
☀️ Test successful - checks-actions |
use std::path::Path; | ||
use toml_edit::ImDocument; | ||
|
||
fn get_span(document: &ImDocument<String>, path: &[&str], get_value: bool) -> Option<Range<usize>> { |
There was a problem hiding this comment.
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>> { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
?
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 | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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::*; |
There was a problem hiding this comment.
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)), |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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?
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( |
There was a problem hiding this comment.
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
let path = path.join("Cargo.toml"); | ||
if let MaybePackage::Package(pkg) = maybe_pkg { | ||
self.emit_lints(pkg, &path)? | ||
} |
There was a problem hiding this comment.
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
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
if tool == "cargo" && !gctx.cli_unstable().cargo_lints { | ||
warn_for_cargo_lint_feature(gctx, warnings); | ||
} |
There was a problem hiding this comment.
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?
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
Would it be possible to update 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? |
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.
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
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
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.