Skip to content

Commit ac6041e

Browse files
committed
Improve diagnostics for the use of unstable library features
- only emits one error/lint (instead of one per missing feature) per usage of unstable and body-unstable items - only emits one future-name-collision lint (instead of one per missing feature) for unstable trait items - makes diagnostics for unstable, soft-unstable, const-unstable, and body-unstable library features translatable, using common subdiagnostic structs. - adds notes with features, reasons, and issue links to const-unstability errors - adds notes with issue links to soft_unstable lints - on nightly, adds `#![feature]` crate attr help to soft_unstable lints - on nightly, adds compiler-upgrade-suggestion notes to const-unstable and soft_unstable diagnostics
1 parent 22fcdf9 commit ac6041e

34 files changed

+501
-322
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,6 @@ const_eval_unstable_in_stable_exposed =
417417
.bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
418418
419419
const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic
420-
.help = add `#![feature({$feature})]` to the crate attributes to enable
421420
422421
const_eval_unterminated_c_string =
423422
reading a null-terminated string starting at {$pointer} with no null found before end of allocation

compiler/rustc_const_eval/src/check_consts/check.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
778778
}) => {
779779
self.check_op(ops::IntrinsicUnstable {
780780
name: intrinsic.name,
781-
features: unstables.iter().map(|u| u.feature).collect(),
781+
features: unstables.iter().map(|u| u.into()).collect(),
782782
const_stable: is_const_stable,
783783
});
784784
}
@@ -862,12 +862,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
862862
// case we need `check_op` to do the check.
863863
let danger_zone = !callee_safe_to_expose_on_stable
864864
&& self.enforce_recursive_const_stability();
865-
let missing_features: SmallVec<[Symbol; 1]> = if danger_zone {
866-
needs_check.map(|u| u.feature).collect()
865+
let missing_features: SmallVec<[_; 1]> = if danger_zone {
866+
needs_check.map(|u| u.into()).collect()
867867
} else {
868868
needs_check
869869
.filter(|u| !is_feature_enabled(u))
870-
.map(|u| u.feature)
870+
.map(|u| u.into())
871871
.collect()
872872
};
873873
if !missing_features.is_empty() {

compiler/rustc_const_eval/src/check_consts/ops.rs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_hir as hir;
77
use rustc_hir::def_id::DefId;
88
use rustc_infer::infer::TyCtxtInferExt;
99
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
10+
use rustc_middle::middle::stability;
1011
use rustc_middle::mir::CallSource;
1112
use rustc_middle::span_bug;
1213
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
@@ -304,38 +305,37 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
304305

305306
/// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
306307
///
307-
/// Contains the names of the features that would allow the use of this function.
308+
/// Contains the names of the features that would allow the use of this function,
309+
/// as well as an optional reason and github issue link for diagnostics.
308310
#[derive(Debug)]
309311
pub(crate) struct FnCallUnstable {
310312
pub def_id: DefId,
311-
pub features: SmallVec<[Symbol; 1]>,
313+
pub features: SmallVec<[stability::EvalDenial; 1]>,
312314
pub safe_to_expose_on_stable: bool,
313315
}
314316

315317
impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
316318
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
317319
Status::Unstable {
318-
gates: self.features.clone(),
320+
gates: self.features.iter().map(|d| d.feature).collect(),
319321
safe_to_expose_on_stable: self.safe_to_expose_on_stable,
320322
is_function_call: true,
321323
}
322324
}
323325

324326
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
325-
let mut err = ccx.dcx().create_err(errors::UnstableConstFn {
327+
let (features, info) = stability::unstable_notes(&self.features);
328+
// Only suggest adding `#![feature]` on nightly.
329+
let nightly_subdiags =
330+
stability::unstable_nightly_subdiags(&ccx.tcx.sess, &self.features, None);
331+
332+
ccx.dcx().create_err(errors::UnstableConstFn {
326333
span,
327334
def_path: ccx.tcx.def_path_str(self.def_id),
328-
});
329-
// FIXME: make this translatable
330-
#[allow(rustc::untranslatable_diagnostic)]
331-
if ccx.tcx.sess.is_nightly_build() {
332-
err.help(format!(
333-
"add `#![feature({})]` to the crate attributes to enable",
334-
self.features.iter().map(Symbol::as_str).intersperse(", ").collect::<String>(),
335-
));
336-
}
337-
338-
err
335+
features,
336+
info,
337+
nightly_subdiags,
338+
})
339339
}
340340
}
341341

@@ -355,18 +355,18 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst {
355355
}
356356
}
357357

358-
/// A call to an intrinsic that is just not const-callable at all.
358+
/// A call to an intrinsic that is const-unstable.
359359
#[derive(Debug)]
360360
pub(crate) struct IntrinsicUnstable {
361361
pub name: Symbol,
362-
pub features: SmallVec<[Symbol; 1]>,
362+
pub features: SmallVec<[stability::EvalDenial; 1]>,
363363
pub const_stable: bool,
364364
}
365365

366366
impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
367367
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
368368
Status::Unstable {
369-
gates: self.features.clone(),
369+
gates: self.features.iter().map(|d| d.feature).collect(),
370370
safe_to_expose_on_stable: self.const_stable,
371371
// We do *not* want to suggest to mark the intrinsic as `const_stable_indirect`,
372372
// that's not a trivial change!
@@ -375,10 +375,17 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
375375
}
376376

377377
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
378+
let (features, info) = stability::unstable_notes(&self.features);
379+
// Only suggest adding `#![feature]` on nightly.
380+
let nightly_subdiags =
381+
stability::unstable_nightly_subdiags(&ccx.tcx.sess, &self.features, None);
382+
378383
ccx.dcx().create_err(errors::UnstableIntrinsic {
379384
span,
380385
name: self.name,
381-
feature: self.features.iter().map(Symbol::as_str).intersperse(", ").collect(),
386+
features,
387+
info,
388+
nightly_subdiags,
382389
})
383390
}
384391
}

compiler/rustc_const_eval/src/errors.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,26 @@ pub(crate) struct UnstableConstFn {
119119
#[primary_span]
120120
pub span: Span,
121121
pub def_path: String,
122+
#[subdiagnostic]
123+
pub features: rustc_middle::error::UnstableLibraryFeatureNote,
124+
#[subdiagnostic]
125+
pub info: Vec<rustc_middle::error::UnstableLibraryFeatureInfo>,
126+
#[subdiagnostic]
127+
pub nightly_subdiags: Vec<rustc_session::errors::NightlyFeatureDiagnostic>,
122128
}
123129

124130
#[derive(Diagnostic)]
125131
#[diag(const_eval_unstable_intrinsic)]
126-
#[help]
127132
pub(crate) struct UnstableIntrinsic {
128133
#[primary_span]
129134
pub span: Span,
130135
pub name: Symbol,
131-
pub feature: String,
136+
#[subdiagnostic]
137+
pub features: rustc_middle::error::UnstableLibraryFeatureNote,
138+
#[subdiagnostic]
139+
pub info: Vec<rustc_middle::error::UnstableLibraryFeatureInfo>,
140+
#[subdiagnostic]
141+
pub nightly_subdiags: Vec<rustc_session::errors::NightlyFeatureDiagnostic>,
132142
}
133143

134144
#[derive(Diagnostic)]

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,6 @@ hir_analysis_missing_trait_item_suggestion = implement the missing item: `{$snip
311311
312312
hir_analysis_missing_trait_item_unstable = not all trait items implemented, missing: `{$missing_item_name}`
313313
.note = default implementation of `{$missing_item_name}` is unstable
314-
.some_note = use of unstable library feature `{$feature}`: {$reason}
315-
.none_note = use of unstable library feature `{$feature}`
316314
317315
hir_analysis_missing_type_params =
318316
the type {$parameterCount ->

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -923,14 +923,7 @@ fn check_impl_items_against_trait<'tcx>(
923923
let full_impl_span = tcx.hir().span_with_body(tcx.local_def_id_to_hir_id(impl_id));
924924
match tcx.eval_default_body_stability(trait_item_id, full_impl_span) {
925925
EvalResult::Deny { denials, .. } => {
926-
for denial in denials {
927-
default_body_is_unstable(
928-
tcx,
929-
full_impl_span,
930-
trait_item_id,
931-
denial.unstability,
932-
);
933-
}
926+
default_body_is_unstable(tcx, full_impl_span, trait_item_id, &denials);
934927
}
935928

936929
// Unmarked default bodies are considered stable (at least for now).

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ use rustc_index::bit_set::BitSet;
8181
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
8282
use rustc_infer::infer::{self, TyCtxtInferExt as _};
8383
use rustc_infer::traits::ObligationCause;
84+
use rustc_middle::middle::stability;
8485
use rustc_middle::query::Providers;
8586
use rustc_middle::ty::error::{ExpectedFound, TypeError};
8687
use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode};
@@ -292,41 +293,22 @@ fn default_body_is_unstable(
292293
tcx: TyCtxt<'_>,
293294
impl_span: Span,
294295
item_did: DefId,
295-
unstability: rustc_attr::Unstability,
296+
denials: &[stability::EvalDenial],
296297
) {
297-
let rustc_attr::Unstability { feature, reason, issue, .. } = unstability;
298298
let missing_item_name = tcx.associated_item(item_did).name;
299-
let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new());
300-
match reason.to_opt_reason() {
301-
Some(r) => {
302-
some_note = true;
303-
reason_str = r.to_string();
304-
}
305-
None => none_note = true,
306-
};
307-
308-
let mut err = tcx.dcx().create_err(errors::MissingTraitItemUnstable {
309-
span: impl_span,
310-
some_note,
311-
none_note,
312-
missing_item_name,
313-
feature,
314-
reason: reason_str,
315-
});
316-
317299
let inject_span = item_did
318300
.as_local()
319301
.and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
320-
rustc_session::parse::add_feature_diagnostics_for_issue(
321-
&mut err,
322-
&tcx.sess,
323-
feature,
324-
rustc_feature::GateIssue::Library(issue),
325-
false,
326-
inject_span,
327-
);
302+
let (features, info) = stability::unstable_notes(denials);
303+
let nightly_subdiags = stability::unstable_nightly_subdiags(&tcx.sess, denials, inject_span);
328304

329-
err.emit();
305+
tcx.dcx().emit_err(errors::MissingTraitItemUnstable {
306+
span: impl_span,
307+
missing_item_name,
308+
features,
309+
info,
310+
nightly_subdiags,
311+
});
330312
}
331313

332314
/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -977,13 +977,13 @@ pub(crate) struct MissingOneOfTraitItem {
977977
pub(crate) struct MissingTraitItemUnstable {
978978
#[primary_span]
979979
pub span: Span,
980-
#[note(hir_analysis_some_note)]
981-
pub some_note: bool,
982-
#[note(hir_analysis_none_note)]
983-
pub none_note: bool,
984980
pub missing_item_name: Symbol,
985-
pub feature: Symbol,
986-
pub reason: String,
981+
#[subdiagnostic]
982+
pub features: rustc_middle::error::UnstableLibraryFeatureNote,
983+
#[subdiagnostic]
984+
pub info: Vec<rustc_middle::error::UnstableLibraryFeatureInfo>,
985+
#[subdiagnostic]
986+
pub nightly_subdiags: Vec<rustc_session::errors::NightlyFeatureDiagnostic>,
987987
}
988988

989989
#[derive(Diagnostic)]

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(array_windows)]
55
#![feature(box_patterns)]
66
#![feature(if_let_guard)]
7+
#![feature(iter_intersperse)]
78
#![feature(let_chains)]
89
#![feature(never_type)]
910
#![feature(try_blocks)]

compiler/rustc_hir_typeck/src/method/probe.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,10 +1327,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
13271327
if let stability::EvalResult::Deny { denials, .. } =
13281328
self.tcx.eval_stability(candidate.item.def_id, None, self.span, None)
13291329
{
1330-
uc.push((
1331-
candidate.clone(),
1332-
denials.iter().map(|d| d.unstability.feature).collect(),
1333-
));
1330+
uc.push((candidate.clone(), denials.iter().map(|d| d.feature).collect()));
13341331
return false;
13351332
}
13361333
true
@@ -1428,10 +1425,11 @@ impl<'tcx> Pick<'tcx> {
14281425
tcx.disabled_nightly_features(
14291426
lint,
14301427
Some(scope_expr_id),
1431-
self.unstable_candidates.iter().flat_map(|(candidate, features)| {
1432-
features.iter().map(|feature| {
1433-
(format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
1434-
})
1428+
self.unstable_candidates.iter().map(|(candidate, features)| {
1429+
(
1430+
format!(" `{}`", tcx.def_path_str(candidate.item.def_id)),
1431+
features.iter().map(Symbol::as_str).intersperse(", ").collect::<String>(),
1432+
)
14351433
}),
14361434
);
14371435
});

compiler/rustc_lint/src/context/diagnostics.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,12 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
382382
BuiltinLintDiag::MacroRuleNeverUsed(n, name) => {
383383
lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag);
384384
}
385-
BuiltinLintDiag::UnstableFeature(msg) => {
386-
lints::UnstableFeature { msg }.decorate_lint(diag);
385+
BuiltinLintDiag::SoftUnstableMacro { features } => {
386+
let denials = features
387+
.iter()
388+
.map(|&(feature, reason, issue)| stability::EvalDenial { feature, reason, issue })
389+
.collect::<Vec<_>>();
390+
stability::soft_unstable(sess, &denials, vec![]).decorate_lint(diag);
387391
}
388392
BuiltinLintDiag::AvoidUsingIntelSyntax => {
389393
lints::AvoidIntelSyntax.decorate_lint(diag);

compiler/rustc_lint/src/lints.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2407,16 +2407,6 @@ pub(crate) struct MacroRuleNeverUsed {
24072407
pub name: Symbol,
24082408
}
24092409

2410-
pub(crate) struct UnstableFeature {
2411-
pub msg: DiagMessage,
2412-
}
2413-
2414-
impl<'a> LintDiagnostic<'a, ()> for UnstableFeature {
2415-
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
2416-
diag.primary_message(self.msg);
2417-
}
2418-
}
2419-
24202410
#[derive(LintDiagnostic)]
24212411
#[diag(lint_avoid_intel_syntax)]
24222412
pub(crate) struct AvoidIntelSyntax;

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
#![warn(unreachable_pub)]
33
// tidy-alphabetical-end
44

5+
use std::num::NonZero;
6+
57
use rustc_abi::ExternAbi;
68
use rustc_ast::node_id::NodeId;
79
use rustc_ast::{AttrId, Attribute};
810
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
911
use rustc_data_structures::stable_hasher::{
1012
HashStable, StableCompare, StableHasher, ToStableHashKey,
1113
};
12-
use rustc_error_messages::{DiagMessage, MultiSpan};
14+
use rustc_error_messages::MultiSpan;
1315
use rustc_hir::def::Namespace;
1416
use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
1517
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
@@ -720,7 +722,10 @@ pub enum BuiltinLintDiag {
720722
MacroIsPrivate(Ident),
721723
UnusedMacroDefinition(Symbol),
722724
MacroRuleNeverUsed(usize, Symbol),
723-
UnstableFeature(DiagMessage),
725+
SoftUnstableMacro {
726+
/// The name, optional reason, and issue number for each disabled unstable feature used.
727+
features: Vec<(Symbol, Option<Symbol>, Option<NonZero<u32>>)>,
728+
},
724729
AvoidUsingIntelSyntax,
725730
AvoidUsingAttSyntax,
726731
IncompleteInclude,

compiler/rustc_middle/messages.ftl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,25 @@ middle_type_length_limit = reached the type-length limit while instantiating `{$
102102
middle_unknown_layout =
103103
the type `{$ty}` has an unknown layout
104104
105+
middle_unstable_library_feature = use of unstable library {$count ->
106+
[one] feature {$features}{$single_feature_has_reason ->
107+
[true] : {$reason_for_single_feature}
108+
*[false] {""}
109+
}
110+
*[other] features {$features}
111+
}
112+
113+
middle_unstable_library_feature_issue =
114+
see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information{$show_feature ->
115+
[true] {" "}about `{$feature}`
116+
*[false] {""}
117+
}
118+
119+
middle_unstable_library_feature_reason = reason for `{$feature}`: {$reason}
120+
121+
middle_unstable_library_feature_suggestion_for_allocator_api =
122+
consider wrapping the inner types in tuple
123+
105124
middle_values_too_big =
106125
values of the type `{$ty}` are too big for the target architecture
107126
middle_written_to_path = the full type name has been written to '{$path}'

0 commit comments

Comments
 (0)