Skip to content

Commit 82ccb65

Browse files
committed
Add --extern-loc to augment unused crate dependency diagnostics
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.
1 parent 9778068 commit 82ccb65

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+512
-47
lines changed

Cargo.lock

+3-2
Original file line numberDiff line numberDiff line change
@@ -726,9 +726,9 @@ dependencies = [
726726

727727
[[package]]
728728
name = "const_fn"
729-
version = "0.4.2"
729+
version = "0.4.3"
730730
source = "registry+https://github.com/rust-lang/crates.io-index"
731-
checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2"
731+
checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
732732

733733
[[package]]
734734
name = "constant_time_eq"
@@ -3914,6 +3914,7 @@ dependencies = [
39143914
"rustc_index",
39153915
"rustc_middle",
39163916
"rustc_parse_format",
3917+
"rustc_serialize",
39173918
"rustc_session",
39183919
"rustc_span",
39193920
"rustc_target",

compiler/rustc_errors/src/diagnostic.rs

+24
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use crate::Level;
44
use crate::Substitution;
55
use crate::SubstitutionPart;
66
use crate::SuggestionStyle;
7+
use crate::ToolMetadata;
78
use rustc_lint_defs::Applicability;
9+
use rustc_serialize::json::Json;
810
use rustc_span::{MultiSpan, Span, DUMMY_SP};
911
use std::fmt;
1012

@@ -303,6 +305,7 @@ impl Diagnostic {
303305
msg: msg.to_owned(),
304306
style: SuggestionStyle::ShowCode,
305307
applicability,
308+
tool_metadata: Default::default(),
306309
});
307310
self
308311
}
@@ -328,6 +331,7 @@ impl Diagnostic {
328331
msg: msg.to_owned(),
329332
style: SuggestionStyle::ShowCode,
330333
applicability,
334+
tool_metadata: Default::default(),
331335
});
332336
self
333337
}
@@ -354,6 +358,7 @@ impl Diagnostic {
354358
msg: msg.to_owned(),
355359
style: SuggestionStyle::CompletelyHidden,
356360
applicability,
361+
tool_metadata: Default::default(),
357362
});
358363
self
359364
}
@@ -408,6 +413,7 @@ impl Diagnostic {
408413
msg: msg.to_owned(),
409414
style,
410415
applicability,
416+
tool_metadata: Default::default(),
411417
});
412418
self
413419
}
@@ -446,6 +452,7 @@ impl Diagnostic {
446452
msg: msg.to_owned(),
447453
style: SuggestionStyle::ShowCode,
448454
applicability,
455+
tool_metadata: Default::default(),
449456
});
450457
self
451458
}
@@ -515,6 +522,23 @@ impl Diagnostic {
515522
self
516523
}
517524

525+
/// Adds a suggestion intended only for a tool. The intent is that the metadata encodes
526+
/// the suggestion in a tool-specific way, as it may not even directly involve Rust code.
527+
pub fn tool_only_suggestion_with_metadata(
528+
&mut self,
529+
msg: &str,
530+
applicability: Applicability,
531+
tool_metadata: Json,
532+
) {
533+
self.suggestions.push(CodeSuggestion {
534+
substitutions: vec![],
535+
msg: msg.to_owned(),
536+
style: SuggestionStyle::CompletelyHidden,
537+
applicability,
538+
tool_metadata: ToolMetadata::new(tool_metadata),
539+
})
540+
}
541+
518542
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
519543
self.span = sp.into();
520544
if let Some(span) = self.span.primary_span() {

compiler/rustc_errors/src/json.rs

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
1414
use crate::emitter::{Emitter, HumanReadableErrorType};
1515
use crate::registry::Registry;
1616
use crate::DiagnosticId;
17+
use crate::ToolMetadata;
1718
use crate::{CodeSuggestion, SubDiagnostic};
1819
use rustc_lint_defs::{Applicability, FutureBreakage};
1920

@@ -180,6 +181,8 @@ struct Diagnostic {
180181
children: Vec<Diagnostic>,
181182
/// The message as rustc would render it.
182183
rendered: Option<String>,
184+
/// Extra tool metadata
185+
tool_metadata: ToolMetadata,
183186
}
184187

185188
#[derive(Encodable)]
@@ -269,6 +272,7 @@ impl Diagnostic {
269272
spans: DiagnosticSpan::from_suggestion(sugg, je),
270273
children: vec![],
271274
rendered: None,
275+
tool_metadata: sugg.tool_metadata.clone(),
272276
});
273277

274278
// generate regular command line output and store it in the json
@@ -312,6 +316,7 @@ impl Diagnostic {
312316
.chain(sugg)
313317
.collect(),
314318
rendered: Some(output),
319+
tool_metadata: ToolMetadata::default(),
315320
}
316321
}
317322

@@ -327,6 +332,7 @@ impl Diagnostic {
327332
.unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)),
328333
children: vec![],
329334
rendered: None,
335+
tool_metadata: ToolMetadata::default(),
330336
}
331337
}
332338
}

compiler/rustc_errors/src/lib.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ use rustc_data_structures::sync::{self, Lock, Lrc};
2323
use rustc_data_structures::AtomicRef;
2424
use rustc_lint_defs::FutureBreakage;
2525
pub use rustc_lint_defs::{pluralize, Applicability};
26+
use rustc_serialize::json::Json;
27+
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
2628
use rustc_span::source_map::SourceMap;
2729
use rustc_span::{Loc, MultiSpan, Span};
2830

2931
use std::borrow::Cow;
32+
use std::hash::{Hash, Hasher};
3033
use std::panic;
3134
use std::path::Path;
3235
use std::{error, fmt};
@@ -73,6 +76,35 @@ impl SuggestionStyle {
7376
}
7477
}
7578

79+
#[derive(Clone, Debug, PartialEq, Default)]
80+
pub struct ToolMetadata(pub Option<Json>);
81+
82+
impl ToolMetadata {
83+
fn new(json: Json) -> Self {
84+
ToolMetadata(Some(json))
85+
}
86+
}
87+
88+
impl Hash for ToolMetadata {
89+
fn hash<H: Hasher>(&self, _state: &mut H) {}
90+
}
91+
92+
// Doesn't really need to round-trip
93+
impl<D: Decoder> Decodable<D> for ToolMetadata {
94+
fn decode(_d: &mut D) -> Result<Self, D::Error> {
95+
Ok(ToolMetadata(None))
96+
}
97+
}
98+
99+
impl<S: Encoder> Encodable<S> for ToolMetadata {
100+
fn encode(&self, e: &mut S) -> Result<(), S::Error> {
101+
match &self.0 {
102+
None => e.emit_unit(),
103+
Some(json) => json.encode(e),
104+
}
105+
}
106+
}
107+
76108
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
77109
pub struct CodeSuggestion {
78110
/// Each substitute can have multiple variants due to multiple
@@ -106,6 +138,8 @@ pub struct CodeSuggestion {
106138
/// which are useful for users but not useful for
107139
/// tools like rustfix
108140
pub applicability: Applicability,
141+
/// Tool-specific metadata
142+
pub tool_metadata: ToolMetadata,
109143
}
110144

111145
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
@@ -775,7 +809,6 @@ impl HandlerInner {
775809
}
776810

777811
let already_emitted = |this: &mut Self| {
778-
use std::hash::Hash;
779812
let mut hasher = StableHasher::new();
780813
diagnostic.hash(&mut hasher);
781814
let diagnostic_hash = hasher.finish();

compiler/rustc_lint/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ rustc_data_structures = { path = "../rustc_data_structures" }
1919
rustc_feature = { path = "../rustc_feature" }
2020
rustc_index = { path = "../rustc_index" }
2121
rustc_session = { path = "../rustc_session" }
22+
rustc_serialize = { path = "../rustc_serialize" }
2223
rustc_trait_selection = { path = "../rustc_trait_selection" }
2324
rustc_parse_format = { path = "../rustc_parse_format" }

compiler/rustc_lint/src/context.rs

+29-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use crate::passes::{EarlyLintPassObject, LateLintPassObject};
2121
use rustc_ast as ast;
2222
use rustc_data_structures::fx::FxHashMap;
2323
use rustc_data_structures::sync;
24-
use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability};
24+
use rustc_errors::{
25+
add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability, SuggestionStyle,
26+
};
2527
use rustc_hir as hir;
2628
use rustc_hir::def::Res;
2729
use rustc_hir::def_id::{CrateNum, DefId};
@@ -32,7 +34,8 @@ use rustc_middle::middle::stability;
3234
use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
3335
use rustc_middle::ty::print::with_no_trimmed_paths;
3436
use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
35-
use rustc_session::lint::BuiltinLintDiagnostics;
37+
use rustc_serialize::json::Json;
38+
use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec};
3639
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
3740
use rustc_session::Session;
3841
use rustc_session::SessionLintStore;
@@ -639,6 +642,30 @@ pub trait LintContext: Sized {
639642
BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => {
640643
db.span_label(span, "the attribute is introduced here");
641644
}
645+
BuiltinLintDiagnostics::ExternDepSpec(krate, loc) => {
646+
let json = match loc {
647+
ExternDepSpec::Json(json) => {
648+
db.help(&format!("remove unnecessary dependency `{}`", krate));
649+
json
650+
}
651+
ExternDepSpec::Raw(raw) => {
652+
db.help(&format!("remove unnecessary dependency `{}` at `{}`", krate, raw));
653+
db.span_suggestion_with_style(
654+
DUMMY_SP,
655+
"raw extern location",
656+
raw.clone(),
657+
Applicability::Unspecified,
658+
SuggestionStyle::CompletelyHidden,
659+
);
660+
Json::String(raw)
661+
}
662+
};
663+
db.tool_only_suggestion_with_metadata(
664+
"json extern location",
665+
Applicability::Unspecified,
666+
json
667+
);
668+
}
642669
}
643670
// Rewrap `db`, and pass control to the user.
644671
decorate(LintDiagnosticBuilder::new(db));

compiler/rustc_lint_defs/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extern crate rustc_macros;
44
pub use self::Level::*;
55
use rustc_ast::node_id::{NodeId, NodeMap};
66
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
7+
use rustc_serialize::json::Json;
78
use rustc_span::edition::Edition;
89
use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol};
910
use rustc_target::spec::abi::Abi;
@@ -239,6 +240,13 @@ impl<HCX> ToStableHashKey<HCX> for LintId {
239240
}
240241
}
241242

243+
// Duplicated from rustc_session::config::ExternDepSpec to avoid cyclic dependency
244+
#[derive(PartialEq)]
245+
pub enum ExternDepSpec {
246+
Json(Json),
247+
Raw(String),
248+
}
249+
242250
// This could be a closure, but then implementing derive trait
243251
// becomes hacky (and it gets allocated).
244252
#[derive(PartialEq)]
@@ -257,6 +265,7 @@ pub enum BuiltinLintDiagnostics {
257265
UnusedDocComment(Span),
258266
PatternsInFnsWithoutBody(Span, Ident),
259267
LegacyDeriveHelpers(Span),
268+
ExternDepSpec(String, ExternDepSpec),
260269
}
261270

262271
/// Lints that are buffered up early on in the `Session` before the

compiler/rustc_metadata/src/creader.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use rustc_index::vec::IndexVec;
1616
use rustc_middle::middle::cstore::{CrateDepKind, CrateSource, ExternCrate};
1717
use rustc_middle::middle::cstore::{ExternCrateSource, MetadataLoaderDyn};
1818
use rustc_middle::ty::TyCtxt;
19+
use rustc_serialize::json::ToJson;
1920
use rustc_session::config::{self, CrateType, ExternLocation};
20-
use rustc_session::lint;
21+
use rustc_session::lint::{self, BuiltinLintDiagnostics, ExternDepSpec};
2122
use rustc_session::output::validate_crate_name;
2223
use rustc_session::search_paths::PathKind;
2324
use rustc_session::{CrateDisambiguator, Session};
@@ -27,6 +28,7 @@ use rustc_span::{Span, DUMMY_SP};
2728
use rustc_target::spec::{PanicStrategy, TargetTriple};
2829

2930
use proc_macro::bridge::client::ProcMacro;
31+
use std::collections::BTreeMap;
3032
use std::path::Path;
3133
use std::{cmp, env};
3234
use tracing::{debug, info};
@@ -871,8 +873,25 @@ impl<'a> CrateLoader<'a> {
871873
// Don't worry about pathless `--extern foo` sysroot references
872874
continue;
873875
}
874-
if !self.used_extern_options.contains(&Symbol::intern(name)) {
875-
self.sess.parse_sess.buffer_lint(
876+
if self.used_extern_options.contains(&Symbol::intern(name)) {
877+
continue;
878+
}
879+
880+
// Got a real unused --extern
881+
let diag = match self.sess.opts.extern_dep_specs.get(name) {
882+
Some(loc) => BuiltinLintDiagnostics::ExternDepSpec(name.clone(), loc.into()),
883+
None => {
884+
// If we don't have a specific location, provide a json encoding of the `--extern`
885+
// option.
886+
let meta: BTreeMap<String, String> =
887+
std::iter::once(("name".to_string(), name.to_string())).collect();
888+
BuiltinLintDiagnostics::ExternDepSpec(
889+
name.clone(),
890+
ExternDepSpec::Json(meta.to_json()),
891+
)
892+
}
893+
};
894+
self.sess.parse_sess.buffer_lint_with_diagnostic(
876895
lint::builtin::UNUSED_CRATE_DEPENDENCIES,
877896
span,
878897
ast::CRATE_NODE_ID,
@@ -881,8 +900,8 @@ impl<'a> CrateLoader<'a> {
881900
name,
882901
self.local_crate_name,
883902
name),
903+
diag,
884904
);
885-
}
886905
}
887906
}
888907

0 commit comments

Comments
 (0)