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

Implement --extern-location #72603

Merged
merged 3 commits into from
Feb 8, 2021
Merged

Implement --extern-location #72603

merged 3 commits into from
Feb 8, 2021

Conversation

jsgf
Copy link
Contributor

@jsgf jsgf commented May 26, 2020

This PR implements --extern-location as a followup to #72342 as part of the implementation of #57274. The goal of this PR is to allow rustc, in coordination with the build system, to present a useful diagnostic about how to remove an unnecessary dependency from a dependency specification file (eg Cargo.toml).

EDIT: Updated to current PR state.

The location is specified for each named crate - that is, for a given --extern foo[=path] there can also be --extern-location foo=<location>. It supports three two styles of location:
~~1. --extern-location foo=file:<path>:<line> - a file path and line specification

  1. --extern-location foo=span:<path>:<start>:<end> - a span specified as a file and start and end byte offsets~~
  2. --extern-location foo=raw:<anything> - a raw string which is included in the output
  3. --extern-location foo=json:<anything> - an arbitrary Json structure which is emitted via Json diagnostics in a tool_metadata field.

1 & 2 are turned into an internal Span, so long as the path exists and is readable, and the location is meaningful (within the file, etc). This is used as the Span for a fix suggestion which is reported like other fix suggestions.

raw and json are for the case where the location isn't best expressed as a file and location within that file. For example, it could be a rule name and the name of a dependency within that rule. rustc makes no attempt to parse the raw string, and simply includes it in the output diagnostic text. json is only included in json diagnostics. raw is emitted as text and also as a json string in tool_metadata.

If no --extern-location option is specified then it will emit a default json structure consisting of {"name": name, "path": path} corresponding to the name and path in --extern name=path.

This is a prototype/RFC to make some of the earlier conversations more concrete. It doesn't stand on its own - it's only useful if implemented by Cargo and other build systems. There's also a ton of implementation details which I'd appreciate a second eye on as well.

NOTE The first commit in this PR is #72342 and should be ignored for the purposes of review. The first commit is a very simplistic implementation which is basically raw-only, presented as a MVP. The second implements the full thing, and subsequent commits are incremental fixes.

cc @ehuss @est31 @petrochenkov @estebank

@rust-highfive
Copy link
Collaborator

r? @matthewjasper

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 26, 2020
@petrochenkov petrochenkov self-assigned this May 26, 2020
@tshepang
Copy link
Member

When I saw -loc, my head said "lines of code". Would it be ok to rename to --extern-location?

@jsgf
Copy link
Contributor Author

jsgf commented May 27, 2020

@tshepang Yes, I have a tendency to over-abbr.

@jsgf jsgf force-pushed the extern-loc branch 2 times, most recently from 9c0c9fe to bd9b98c Compare May 27, 2020 06:12
@rust-highfive

This comment has been minimized.

@jsgf jsgf changed the title Implement --extern-loc Implement --extern-location May 27, 2020
@jsgf jsgf force-pushed the extern-loc branch 2 times, most recently from 2951ba5 to bf1c504 Compare May 27, 2020 23:28
@rust-highfive

This comment has been minimized.

@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux mingw-check
##[section]Starting: Initialize job
Agent name: 'Azure Pipelines 65'
Agent machine name: 'fv-az578'
Current agent version: '2.168.2'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https://github.com/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.2)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/fb540377-1c06-4650-9516-8e45d0174c18.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/72603/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https://github.com/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/72603/merge:refs/remotes/pull/72603/merge
---
 ---> 3adb0605cc65
Step 6/7 : ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
 ---> Using cache
 ---> 28dbc326cb7f
Step 7/7 : ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors &&            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu &&            python3 ../x.py build --stage 0 src/tools/build-manifest &&            python3 ../x.py test --stage 0 src/tools/compiletest &&            python3 ../x.py test src/tools/tidy &&            python3 ../x.py doc --stage 0 src/libstd &&            /scripts/validate-toolstate.sh
 ---> 537a01811900
Successfully built 537a01811900
Successfully tagged rust-ci:latest
Built container sha256:537a018119009dc218456238dec90b5530050db1e2a1e166550c218003f6159d
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-rust-ir v0.10.0
    Checking chalk-solve v0.10.0
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
configure: llvm.assertions      := True
configure: dist.missing-tools   := True
configure: rust.codegen-units-std := 1
configure: rust.verify-llvm-ir  := True
configure: build.configure-args := ['--enable-sccache', '--disable-manage-submodu ...
configure: writing `config.toml` in current directory
configure: 
configure: run `python /checkout/x.py --help`
configure: 
---
Hugepagesize:       2048 kB
DirectMap4k:      147392 kB
DirectMap2M:     2998272 kB
DirectMap1G:     6291456 kB
+ python3 ../x.py test src/tools/expand-yaml-anchors
Ensuring the YAML anchors in the GitHub Actions config were expanded
Ensuring the YAML anchors in the GitHub Actions config were expanded
Building stage0 tool expand-yaml-anchors (x86_64-unknown-linux-gnu)
   Compiling unicode-xid v0.2.0
   Compiling syn v1.0.11
   Compiling linked-hash-map v0.5.2
   Compiling lazy_static v1.4.0
   Compiling lazy_static v1.4.0
   Compiling yaml-rust v0.4.3
   Compiling quote v1.0.2
   Compiling thiserror-impl v1.0.5
   Compiling thiserror v1.0.5
   Compiling yaml-merge-keys v0.4.0
   Compiling expand-yaml-anchors v0.1.0 (/checkout/src/tools/expand-yaml-anchors)
Build completed successfully in 0:00:32
+ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu
    Finished dev [unoptimized] target(s) in 0.21s
Checking rustdoc artifacts (x86_64-unknown-linux-gnu -> i686-pc-windows-gnu)
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking chalk-rust-ir v0.10.0
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-solve v0.10.0
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
skip untracked path cpu-usage.csv during rustfmt invocations
skip untracked path src/doc/book/ during rustfmt invocations
skip untracked path src/doc/rust-by-example/ during rustfmt invocations
skip untracked path src/llvm-project/ during rustfmt invocations
Diff in /checkout/src/librustc_lint/context.rs at line 31:
 use rustc_middle::middle::stability;
 use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
 use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
+use rustc_session::config::ExternDepSpec;
 use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics};
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
Diff in /checkout/src/librustc_lint/context.rs at line 37:
Diff in /checkout/src/librustc_lint/context.rs at line 37:
-use rustc_session::config::ExternDepSpec;
 use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
 use rustc_target::abi::LayoutOf;
 
Running `"/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/rustfmt" "--config-path" "/checkout" "--edition" "2018" "--unstable-features" "--skip-children" "--check" "/checkout/src/librustc_lint/context.rs"` failed.
If you're running `tidy`, try again with `--bless`. Or, if you just want to format code, run `./x.py fmt` instead.
Build completed unsuccessfully in 0:00:38
== clock drift check ==
  local time: Thu May 28 23:43:15 UTC 2020
  network time: Thu, 28 May 2020 23:43:16 GMT
  network time: Thu, 28 May 2020 23:43:16 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/72603/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/72603/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (3649) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@petrochenkov petrochenkov added T-cargo Relevant to the cargo team, which will review and decide on the PR/issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 30, 2020
@petrochenkov
Copy link
Contributor

petrochenkov commented May 30, 2020

This is great, thank you.
Diagnostic spans pointing to build system files in particular.
I'll nominate this for the compiler and cargo teams because this needs feedback from some wider audience.
(One question in particular is what process to use - MCP? RFC?)

I'll leave a few notes and questions in comments below.

@petrochenkov
Copy link
Contributor

Note 1 - Motivation

This is useful not only for the unused_crate_dependencies lint, but for any kind of diagnostics that want to show the point where a crate name was introduced.

All "cannot find or load crate" errors, name ambiguity errors during resolution, lint for private dependencies (#44663), perhaps something else.

@petrochenkov
Copy link
Contributor

Note 2 - Dependency tracking

When file is loaded into source map during conversion into a span that file also becomes a dependency for the build.
It's emitted into the depinfo files (.d) used by cargo for rebuilds.

Looks like PR puts the files specified in --extern-location into source map lazily, only if an error requiring these spans actually happens.
This is great because, AFAIK, cargo doesn't rebuild anything when you touch Cargo.toml without introducing some real changes.
If Cargo.toml is put into depinfo, then the rebuilds will happen, unless cargo specifically removes it from dependencies.

Lazy loading may be inconvenient in some cases when we want to work with spans uniformly, though.
E.g. you cannot put an item span and a span from --extern-location together into a Vec<Span> without loading the file and adding it to dependencies.

@petrochenkov
Copy link
Contributor

Note 3 - Relative path

If the path in --extern-location foo=file:bar.mk is relative, what should be considered the base directory?

Will cargo and other tools use absolute or relative paths in practice?

@petrochenkov
Copy link
Contributor

Note 4 - Command line length compression

Somewhat related to note 3.

If tools start using absolute paths for --extern-location, e.g. --extern-location foo=span:/my/full/path/Cargo.toml:23:32 rather than --extern-location foo=span:Cargo.toml:23:32, then it will be easy to make the command line very long and hit some limits?

In practice all the paths from cargo will refer to the same Cargo.toml, but different byte ranges inside it.
So, some compression scheme could be useful.
--extern-location-file my_file=/my/full/path/Cargo.toml --extern-location foo=my_file:23:32 --extern-location bar=my_file:34:43.

@petrochenkov
Copy link
Contributor

Note 5 - Over-engineering

Are 4 types of extern locations really necessary?
(Leaving this for the teams' feedback.)

@petrochenkov petrochenkov added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 30, 2020
@petrochenkov petrochenkov removed their assignment May 30, 2020
@jsgf
Copy link
Contributor Author

jsgf commented May 31, 2020

@petrochenkov Thank you very much for taking the time to look at this.

1: Yes, I hadn't really considered other uses, but I'm glad there are some.

2: I was concerned about the Spans becoming part of the state of the build, both as dependencies, and for incremental builds. I wasn't sure if it was desirable or not, or what the effects would be. It sounds like it's not an issue, and even desirable.

3: I think the path should be relative to rustc cwd, since Cargo (or build system in general) control both the working directory and the paths. They could be absolute, but that would be very redundant. They're also subject to --remap-path-prefix which I think is expected.

4: I was a little concerned about command-line expansion, but ultimately decided that it isn't an issue we need to solve now - it could be added later if experience shows that it's necessary. It's proportional to the number of direct dependencies a crate has, which doesn't seem to be large - I haven't done a systematic survey, but my gut says that it's going to be almost always <50. It would be a different matter if it also affected indirect dependencies. If it is needed, then I'm thinking of some scheme of adding a new option to give short ids to paths, and a new location type to reference them, eg, --extern-location-path 0:foo/Cargo.toml --extern-location spanref:0:234:567.

5: I have a local change to remove raw, since it's a strict subset of json. I'm using json for Buck, and I'd expect cargo to use span if it's going to use it at all, which makes file redundant - but it seems like the easiest to use for simpler cases. (This is also one of the reasons to defer a command-line option compression scheme for now.)

One thing I'd like to highlight is that the json support includes a schema change for the json diagnostics by adding a tool_metadata field. The intent is to allow json to be round-tripped through rustc without needing to invent a new encoding/serialization/quoting scheme. It's currently good enough for me to prototype with, but I'd like guidance as to whether there's a better way to handle this (if nothing else I wonder if it should be on just the top-level diagnostic, rather than every child diagnostic).

@jsgf
Copy link
Contributor Author

jsgf commented Jun 2, 2020

Thinking about raw vs json a bit more - the json type is only emitted in the json format diagnostics and never appears in the human-visible rendered form. But if you want to present something to the user as advice that's not a file coordinate, then it would be useful to have a text type for that...

@nikomatsakis
Copy link
Contributor

@bors delegate+

I'm delegating to @jsgf to do r=nikomatsakis once the nit is addressed.

@bors
Copy link
Contributor

bors commented Feb 6, 2021

✌️ @jsgf can now approve this pull request

@bors
Copy link
Contributor

bors commented Feb 7, 2021

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

This allows a build system to indicate a location in its own dependency
specification files (eg Cargo's `Cargo.toml`) which can be reported
along side any unused crate dependency.

This supports several types of location:
 - 'json' - provide some json-structured data, which is included in the json diagnostics
     in a `tool_metadata` field
 - 'raw' - emit the provided string into the output. This also appears as a json string in
     `tool_metadata`.

If no `--extern-location` is explicitly provided then a default json entry of the form
`"tool_metadata":{"name":<cratename>,"path":<cratepath>}` is emitted.
...so we can skip serializing `tool_metadata` if it hasn't been set.
This makes the output a bit cleaner, and avoiding having to update a
bunch of unrelated tests.
This will make sure the encoder will get updated if any new fields are
added to Diagnostic.
@jsgf
Copy link
Contributor Author

jsgf commented Feb 7, 2021

@bors r+

@bors
Copy link
Contributor

bors commented Feb 7, 2021

📌 Commit 91d8c3b has been approved by jsgf

@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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 7, 2021
@jsgf
Copy link
Contributor Author

jsgf commented Feb 7, 2021

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Feb 7, 2021

💡 This pull request was already approved, no need to approve it again.

@bors
Copy link
Contributor

bors commented Feb 7, 2021

📌 Commit 91d8c3b has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Feb 8, 2021

⌛ Testing commit 91d8c3b with merge 0b7a598...

@bors
Copy link
Contributor

bors commented Feb 8, 2021

☀️ Test successful - checks-actions
Approved by: nikomatsakis
Pushing 0b7a598 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Feb 8, 2021
@bors bors merged commit 0b7a598 into rust-lang:master Feb 8, 2021
@rustbot rustbot added this to the 1.52.0 milestone Feb 8, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 4, 2021
…crum

Add an unstable --json=unused-externs flag to print unused externs

This adds an unstable flag to print a list of the extern names not used by cargo.

This PR will enable cargo to collect unused dependencies from all units and provide warnings.
The companion PR to cargo is: rust-lang/cargo#8437

The goal is eventual stabilization of this flag in rustc as well as in cargo.

Discussion of this feature is mostly contained inside these threads: rust-lang#57274 rust-lang#72342 rust-lang#72603

The feature builds upon the internal datastructures added by rust-lang#72342

Externs are uniquely identified by name and the information is sufficient for cargo.
If the mode is enabled, rustc will print json messages like:

```
{"unused_extern_names":["byteorder","openssl","webpki"]}
```

For a crate that got passed byteorder, openssl and webpki dependencies but needed none of them.

### Q: Why not pass -Wunused-crate-dependencies?
A: See [ehuss's comment here](rust-lang#57274 (comment))
   TLDR: it's cleaner. Rust's warning system wasn't built to be filtered or edited by cargo.
   Even a basic implementation of the feature would have to change the "n warnings emitted" line that rustc prints at the end.
   Cargo ideally wants to synthesize its own warnings anyways. For example, it would be hard for rustc to emit warnings like
   "dependency foo is only used by dev targets", suggesting to make it a dev-dependency instead.

### Q: Make rustc emit used or unused externs?
A: Emitting used externs has the advantage that it simplifies cargo's collection job.
   However, emitting unused externs creates less data to be communicated between rustc and cargo.
   Often you want to paste a cargo command obtained from `cargo build -vv` for doing something
   completely unrelated. The message is emitted always, even if no warning or error is emitted.
   At that point, even this tiny difference in "noise" matters. That's why I went with emitting unused externs.

### Q: One json msg per extern or a collective json msg?
A: Same as above, the data format should be concise. Having 30 lines for the 30 crates a crate uses would be disturbing to readers.
   Also it helps the cargo implementation to know that there aren't more unused deps coming.

### Q: Why use names of externs instead of e.g. paths?
A: Names are both sufficient as well as neccessary to uniquely identify a passed `--extern` arg.
   Names are sufficient because you *must* pass a name when passing an `--extern` arg.
   Passing a path is optional on the other hand so rustc might also figure out a crate's location from the file system.
   You can also put multiple paths for the same extern name, via e.g. `--extern hello=/usr/lib/hello.rmeta --extern hello=/usr/local/lib/hello.rmeta`,
   but rustc will only ever use one of those paths.
   Also, paths don't identify a dependency uniquely as it is possible to have multiple different extern names point to the same path.
   So paths are ill-suited for identification.

### Q: What about 2015 edition crates?
A: They are fully supported.
   Even on the 2015 edition, an explicit `--extern` flag is is required to enable `extern crate foo;` to work (outside of sysroot crates, which this flag doesn't warn about anyways).
   So the lint would still fire on 2015 edition crates if you haven't included a dependency specified in Cargo.toml using `extern crate foo;` or similar.
   The lint won't fire if your sole use in the crate is through a `extern crate foo;`   statement, but that's not its job.
   For detecting unused `extern crate foo` statements, there is the `unused_extern_crates` lint
   which can be enabled by `#![warn(unused_extern_crates)]` or similar.

cc `@jsgf` `@ehuss` `@petrochenkov` `@estebank`
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 4, 2021
…crum

Add an unstable --json=unused-externs flag to print unused externs

This adds an unstable flag to print a list of the extern names not used by cargo.

This PR will enable cargo to collect unused dependencies from all units and provide warnings.
The companion PR to cargo is: rust-lang/cargo#8437

The goal is eventual stabilization of this flag in rustc as well as in cargo.

Discussion of this feature is mostly contained inside these threads: rust-lang#57274 rust-lang#72342 rust-lang#72603

The feature builds upon the internal datastructures added by rust-lang#72342

Externs are uniquely identified by name and the information is sufficient for cargo.
If the mode is enabled, rustc will print json messages like:

```
{"unused_extern_names":["byteorder","openssl","webpki"]}
```

For a crate that got passed byteorder, openssl and webpki dependencies but needed none of them.

### Q: Why not pass -Wunused-crate-dependencies?
A: See [ehuss's comment here](rust-lang#57274 (comment))
   TLDR: it's cleaner. Rust's warning system wasn't built to be filtered or edited by cargo.
   Even a basic implementation of the feature would have to change the "n warnings emitted" line that rustc prints at the end.
   Cargo ideally wants to synthesize its own warnings anyways. For example, it would be hard for rustc to emit warnings like
   "dependency foo is only used by dev targets", suggesting to make it a dev-dependency instead.

### Q: Make rustc emit used or unused externs?
A: Emitting used externs has the advantage that it simplifies cargo's collection job.
   However, emitting unused externs creates less data to be communicated between rustc and cargo.
   Often you want to paste a cargo command obtained from `cargo build -vv` for doing something
   completely unrelated. The message is emitted always, even if no warning or error is emitted.
   At that point, even this tiny difference in "noise" matters. That's why I went with emitting unused externs.

### Q: One json msg per extern or a collective json msg?
A: Same as above, the data format should be concise. Having 30 lines for the 30 crates a crate uses would be disturbing to readers.
   Also it helps the cargo implementation to know that there aren't more unused deps coming.

### Q: Why use names of externs instead of e.g. paths?
A: Names are both sufficient as well as neccessary to uniquely identify a passed `--extern` arg.
   Names are sufficient because you *must* pass a name when passing an `--extern` arg.
   Passing a path is optional on the other hand so rustc might also figure out a crate's location from the file system.
   You can also put multiple paths for the same extern name, via e.g. `--extern hello=/usr/lib/hello.rmeta --extern hello=/usr/local/lib/hello.rmeta`,
   but rustc will only ever use one of those paths.
   Also, paths don't identify a dependency uniquely as it is possible to have multiple different extern names point to the same path.
   So paths are ill-suited for identification.

### Q: What about 2015 edition crates?
A: They are fully supported.
   Even on the 2015 edition, an explicit `--extern` flag is is required to enable `extern crate foo;` to work (outside of sysroot crates, which this flag doesn't warn about anyways).
   So the lint would still fire on 2015 edition crates if you haven't included a dependency specified in Cargo.toml using `extern crate foo;` or similar.
   The lint won't fire if your sole use in the crate is through a `extern crate foo;`   statement, but that's not its job.
   For detecting unused `extern crate foo` statements, there is the `unused_extern_crates` lint
   which can be enabled by `#![warn(unused_extern_crates)]` or similar.

cc ``@jsgf`` ``@ehuss`` ``@petrochenkov`` ``@estebank``
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 4, 2021
…crum

Add an unstable --json=unused-externs flag to print unused externs

This adds an unstable flag to print a list of the extern names not used by cargo.

This PR will enable cargo to collect unused dependencies from all units and provide warnings.
The companion PR to cargo is: rust-lang/cargo#8437

The goal is eventual stabilization of this flag in rustc as well as in cargo.

Discussion of this feature is mostly contained inside these threads: rust-lang#57274 rust-lang#72342 rust-lang#72603

The feature builds upon the internal datastructures added by rust-lang#72342

Externs are uniquely identified by name and the information is sufficient for cargo.
If the mode is enabled, rustc will print json messages like:

```
{"unused_extern_names":["byteorder","openssl","webpki"]}
```

For a crate that got passed byteorder, openssl and webpki dependencies but needed none of them.

### Q: Why not pass -Wunused-crate-dependencies?
A: See [ehuss's comment here](rust-lang#57274 (comment))
   TLDR: it's cleaner. Rust's warning system wasn't built to be filtered or edited by cargo.
   Even a basic implementation of the feature would have to change the "n warnings emitted" line that rustc prints at the end.
   Cargo ideally wants to synthesize its own warnings anyways. For example, it would be hard for rustc to emit warnings like
   "dependency foo is only used by dev targets", suggesting to make it a dev-dependency instead.

### Q: Make rustc emit used or unused externs?
A: Emitting used externs has the advantage that it simplifies cargo's collection job.
   However, emitting unused externs creates less data to be communicated between rustc and cargo.
   Often you want to paste a cargo command obtained from `cargo build -vv` for doing something
   completely unrelated. The message is emitted always, even if no warning or error is emitted.
   At that point, even this tiny difference in "noise" matters. That's why I went with emitting unused externs.

### Q: One json msg per extern or a collective json msg?
A: Same as above, the data format should be concise. Having 30 lines for the 30 crates a crate uses would be disturbing to readers.
   Also it helps the cargo implementation to know that there aren't more unused deps coming.

### Q: Why use names of externs instead of e.g. paths?
A: Names are both sufficient as well as neccessary to uniquely identify a passed `--extern` arg.
   Names are sufficient because you *must* pass a name when passing an `--extern` arg.
   Passing a path is optional on the other hand so rustc might also figure out a crate's location from the file system.
   You can also put multiple paths for the same extern name, via e.g. `--extern hello=/usr/lib/hello.rmeta --extern hello=/usr/local/lib/hello.rmeta`,
   but rustc will only ever use one of those paths.
   Also, paths don't identify a dependency uniquely as it is possible to have multiple different extern names point to the same path.
   So paths are ill-suited for identification.

### Q: What about 2015 edition crates?
A: They are fully supported.
   Even on the 2015 edition, an explicit `--extern` flag is is required to enable `extern crate foo;` to work (outside of sysroot crates, which this flag doesn't warn about anyways).
   So the lint would still fire on 2015 edition crates if you haven't included a dependency specified in Cargo.toml using `extern crate foo;` or similar.
   The lint won't fire if your sole use in the crate is through a `extern crate foo;`   statement, but that's not its job.
   For detecting unused `extern crate foo` statements, there is the `unused_extern_crates` lint
   which can be enabled by `#![warn(unused_extern_crates)]` or similar.

cc ```@jsgf``` ```@ehuss``` ```@petrochenkov``` ```@estebank```
jsgf added a commit to jsgf/rust that referenced this pull request Apr 15, 2022
`--extern-location` was an experiment to investigate the best way to
generate useful diagnostics for unused dependency warnings by enabling a
build system to identify the corresponding build config.

While I did successfully use this, I've since been convinced the
alternative `--json unused-externs` mechanism is the way to go, and
there's no point in having two mechanisms with basically the same
functionality.

This effectively reverts rust-lang#72603
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request Apr 19, 2022
…dtwco

Remove `--extern-location` and all associated code

`--extern-location` was an experiment to investigate the best way to
generate useful diagnostics for unused dependency warnings by enabling a
build system to identify the corresponding build config.

While I did successfully use this, I've since been convinced the
alternative `--json unused-externs` mechanism is the way to go, and
there's no point in having two mechanisms with basically the same
functionality.

This effectively reverts rust-lang#72603
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request Apr 19, 2022
…dtwco

Remove `--extern-location` and all associated code

`--extern-location` was an experiment to investigate the best way to
generate useful diagnostics for unused dependency warnings by enabling a
build system to identify the corresponding build config.

While I did successfully use this, I've since been convinced the
alternative `--json unused-externs` mechanism is the way to go, and
there's no point in having two mechanisms with basically the same
functionality.

This effectively reverts rust-lang#72603
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request Apr 19, 2022
…dtwco

Remove `--extern-location` and all associated code

`--extern-location` was an experiment to investigate the best way to
generate useful diagnostics for unused dependency warnings by enabling a
build system to identify the corresponding build config.

While I did successfully use this, I've since been convinced the
alternative `--json unused-externs` mechanism is the way to go, and
there's no point in having two mechanisms with basically the same
functionality.

This effectively reverts rust-lang#72603
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-cargo Relevant to the cargo team, which will review and decide on the PR/issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.