-
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
Lint against unexpected cfgs in [target.'cfg(...)']
#14581
base: master
Are you sure you want to change the base?
Conversation
@@ -56,6 +57,80 @@ pub struct TargetInfo { | |||
pub rustdocflags: Rc<[String]>, | |||
} | |||
|
|||
/// Check Config (aka `--print=check-cfg` representation) | |||
#[derive(Debug, Default, Clone)] | |||
pub struct CheckCfg { |
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 this live in cargo-platform
?
A valid answer can "maybe later" so we can keep it close for now as we figure out what this should look like
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.
Idk, but one thing to keep in mind is that --print=check-cfg
is still unstable and I don't if we should expose unstable rustc parts in cargo-platform
.
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.
While that flag is unstable, the formatting of check-cfg's is already stable and this is using the same format, right?
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.
Negative, the input of --check-cfg
and the output of --print=check-cfg
are different.
I choose to make the output of --print=check-cfg
very close to --print=cfg
, so it would be easier to parse for consumer.
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.
As suggested I put those in cargo-platform
for now. f8179c5
(#14581)
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 choose to make the output of --print=check-cfg very close to --print=cfg, so it would be easier to parse for consumer.
Is there a place that documents differences?
I worry that having check-cfg and print-cfg be different will make things more difficult, not less, as we now have two distinct ways of talking about the same kind of thing.
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.
Yes, the documentation of --print=check-cfg
, on the left is the --check-cfg
input and on the right is the --print=check-cfg
output.
My concern about re-using the --check-cfg
syntax is that it's not easy to parse and would basically require every user to have a MetaItem parser, while with the simplified output you only need to split the first =
and strip the "
for the value, much easier.
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'm concerned we may add support for something that isn't expressible in this syntax, e.g. https://internals.rust-lang.org/t/pre-rfc-mutually-excusive-global-features/19618#additional-globalsvalues-validation-rules-24
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.
imo the name --print=check-cfg
needs to change because that is not what its doing (but printing another format with the same intent) and giving it this name is confusing as I look through the PR. I know in the context of this PR what it means now but in viewing the code as someone without that context, that isn't clear at all.
Similarly, this is terrible UI design for rustc's command-line. The user has to not just read the docs but carefully parse a bullet list to try to understand that this is not check-cfg thats being printed.
let check_cfg = { | ||
let mut process = rustc.workspace_process(); | ||
|
||
apply_env_config(gctx, &mut process)?; | ||
process | ||
.arg("-") | ||
.arg("--print=check-cfg") | ||
.arg("--check-cfg=cfg()") | ||
.arg("-Zunstable-options") | ||
.env_remove("RUSTC_LOG"); | ||
|
||
// Removes `FD_CLOEXEC` set by `jobserver::Client` to pass jobserver | ||
// as environment variables specify. | ||
if let Some(client) = gctx.jobserver_from_env() { | ||
process.inherit_jobserver(client); | ||
} | ||
|
||
let (output, _error) = rustc | ||
.cached_output(&process, 0) | ||
.with_context(|| "failed to run `rustc` to learn about check-cfg information")?; | ||
|
||
let lines = output.lines(); | ||
|
||
lines.fold(CheckCfg::default(), |mut check_cfg, line| { | ||
check_cfg.process_line(line); | ||
check_cfg | ||
}) | ||
}; |
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.
The feature voluntarily does not follow RUSTFLAGS (to avoid mistakes with one-off and for consistency) but instead follows Rust unexpected_cfgs lint configuration:
Could you clarify what you mean by this?
Calling rustc is "expensive" for no-op builds which is especially important for cargo-script. Yes, this is cached, but I'd like to better understand why we need this over adding it to another call.
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 want to avoid confusing users who might add --check-cfg
args in RUSTFLAGS
, thinking that it works while at the second they will remove them from RUSTFLAGS
it will not anymore; but now that I think about it more, it's already the case for rustc
(since it gets RUSTFLAGS
), so this behaviour would be worse than not forwarding it as it would work with rustc
but not Cargo.
I will revert this behaviour and merge it into the others --print
s.
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.
Having this respect RUSTFLAGS would likely require this to be processed during compilation. Properly processing RUSTFLAGS is a complex, expensive operation as it is fixed point as it evaluates how new --cfg
s opt-in more RUSTFLAGS.
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.
TargetInfo
as some handling of RUSTFLAGS
, so we now use it's handling of RUSTFLAGS
. 18f2caf
(#14581)
let mut process = rustc.workspace_process(); | ||
|
||
apply_env_config(gctx, &mut process)?; | ||
process | ||
.arg("-") | ||
.arg("--print=check-cfg") | ||
.arg("--check-cfg=cfg()") | ||
.arg("-Zunstable-options") | ||
.env_remove("RUSTC_LOG"); | ||
|
||
// Removes `FD_CLOEXEC` set by `jobserver::Client` to pass jobserver | ||
// as environment variables specify. | ||
if let Some(client) = gctx.jobserver_from_env() { | ||
process.inherit_jobserver(client); | ||
} | ||
|
||
let (output, _error) = rustc | ||
.cached_output(&process, 0) | ||
.with_context(|| "failed to run `rustc` to learn about check-cfg information")?; |
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: I've not yet inspected this for alignment with our other rustc calls
src/cargo/ops/cargo_compile/mod.rs
Outdated
gctx.shell().warn(format!( | ||
"{prefix}unexpected `cfg` condition value: `{value}` for `{cfg}` in `[target.'cfg({cfg_expr})'{suffix}]`" | ||
))?; | ||
} | ||
None => { | ||
gctx.shell().warn(format!( | ||
"{prefix}unexpected `cfg` condition name: `{name}`{cfg} in `[target.'cfg({cfg_expr})'{suffix}]`", | ||
cfg = if value.is_some() { | ||
format!(" for `{cfg}`") | ||
} else { | ||
format!("") | ||
} | ||
))?; | ||
} |
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.
As this aligns closely with our new linting system, I'm assuming @Muscraft would prefer these to be using annotate-snippets
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.
The last revision of this PR is now using annotate-snippets.
This probably could have used a conversation with the Cargo team first to ensure alignment with where we are going with lints. If there are questions about the direction I'm pointing this towards, feel free to reach out! |
src/cargo/ops/cargo_compile/mod.rs
Outdated
if lint_rustflags.iter().any(|a| a == "--check-cfg") { | ||
Ok(Some(CheckCfg::new(gctx, rustc, Some(lint_rustflags))?)) |
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 feel comfortable having us parse a command line
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 guess we don't parse it but forward it to rustc but then we are conditionally including unrelated flags in the call to rustc which feels odd and potentially brittle
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.
Yeah, it's a hack and I don't really like it either, but we just deduplicated all the logic in #14567, that I didn't wanted to reintroduce one that will potentially stay indefinitely and the unrelated flags are only allow/warn/deny which is fine for --print=check-cfg
.
ad80bc5
to
18f2caf
Compare
18f2caf
to
abc9f45
Compare
src/cargo/util/lints.rs
Outdated
// FIXME: While it doesn't doesn't really matter for `--print=check-cfg`, wee should | ||
// still pass the actual requested targets instead of an empty array so that the | ||
// target info can be de-duplicated. |
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.
How are we tracking this?
src/cargo/util/lints.rs
Outdated
return Ok(()); | ||
}; | ||
|
||
// If we have extra `--check-cfg` args comming from the lints config, we need to | ||
// refetch the `--print=check-cfg` with those extra args. | ||
let lint_rustflags = pkg.manifest().lint_rustflags(); | ||
let check_cfg = if lint_rustflags.iter().any(|a| a == "--check-cfg") { | ||
Some(target_info.check_cfg_with_extra_args(gctx, &rustc, lint_rustflags)?) |
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.
Is there a reason we are doing both a "global" and " extra_args" calls rather than just the latter?
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.
The "global" call is always done, cached and already contains the --print=check-cfg
, so getting it is mostly "free", and even if we don't use the print-check-cfg it parsed it is still useful as it gives us all the RUSTFLAGS
args that we can just re-use for free to the "extra_args" case.
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.
If we don't need to ask for it from rustc twice, we probably shouldn't. Removing the global check_cfg seems like it would simplify this overall PR as we no longer need to interject ourselves into the regular rustc query, even if we still need it for processing rustflags.
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.
Well we don't really ask for rustc twice, we are just re-using a already available information for when there is no [lints.rust.unexpected_cfgs.check-cfg]
specified, which I think is the vast majority of projects.
The same "global" call will be used for cfgs, split-debuginfo support, ...
Schematically, if we have 3 packages, we would currently do 1 rustc
call for the "global".
Now imaging that one the package as extra check-cfg args from the lint config, with the current strategy we would only do 2 rustc
calls, the "global" and the one specialized for the package.
If we were to not "interject ourselves into the regular rustc query" (aka "global") there would 4 rustc
calls (the global and 1 per package) instead of 2.
Btw I'm getting mixed signal with this comment and the one this conversation:
Calling rustc is "expensive" for no-op builds which is especially important for cargo-script. Yes, this is cached, but I'd like to better understand why we need this over adding it to another call.
Which is why I changed strategy to reduce the number of calls.
8855ab4
to
dbc30b8
Compare
☔ The latest upstream changes (presumably #14630) made this pull request unmergeable. Please resolve the merge conflicts. |
da4efc6
to
fd48e03
Compare
fd48e03
to
438b97d
Compare
☔ The latest upstream changes (presumably #14137) made this pull request unmergeable. Please resolve the merge conflicts. |
with the help of the Cargo lints infra
438b97d
to
5cecf3c
Compare
☔ The latest upstream changes (presumably #14752) made this pull request unmergeable. Please resolve the merge conflicts. |
What does this PR try to resolve?
Since #13571, we now lint by default on unexpected cfgs in Rust code, but one area that is missing are cfg expressions in
Cargo.toml
and.cargo/config.toml
. This PR address this missing piece by (unstably) introducing-Zcheck-target-cfgs
based onrustc --print=check-cfg
.The feature
voluntarily does not always followbut instead follows RustRUSTFLAGS
(to avoid mistakes with one-off and for consistency)unexpected_cfgs
lint configuration:In terms of implementation, one global
--print=check-cfg
is retrieved and fetched, packages that have their owncheck-cfg
inputs have each their own--print=check-cfg
, otherwise the expected cfgs will get mixed up. Each--print=check-cfg
is cached and only done if necessary.How should we test and review this PR?
As asked I tried putting everything into small commits, they may not reflect the de-coupling possible as they follow the implementation but they are still IMO very useful. However I strongly recommand doing a first-pass of the complete diff first.
In terms of tests, I added some combinations that I though would be useful, I can add more if desired.
Additional information
Documentation for
--print=check-cfg
. It was one of motivation of the compiler MCP rust-lang/compiler-team#743.r? @epage