Skip to content

Commit 718706f

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 f36aeec commit 718706f

35 files changed

+512
-330
lines changed

compiler/rustc_const_eval/messages.ftl

-1
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,6 @@ const_eval_unstable_in_stable_exposed =
420420
.bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval)
421421
422422
const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic
423-
.help = add `#![feature({$feature})]` to the crate attributes to enable
424423
425424
const_eval_unterminated_c_string =
426425
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

+4-4
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
741741
}) => {
742742
self.check_op(ops::IntrinsicUnstable {
743743
name: intrinsic.name,
744-
features: unstables.iter().map(|u| u.feature).collect(),
744+
features: unstables.iter().map(|u| u.into()).collect(),
745745
const_stable_indirect: *const_stable_indirect,
746746
});
747747
}
@@ -823,12 +823,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
823823
// case we need `check_op` to do the check.
824824
let danger_zone = !callee_safe_to_expose_on_stable
825825
&& self.enforce_recursive_const_stability();
826-
let missing_features: SmallVec<[Symbol; 1]> = if danger_zone {
827-
needs_check.map(|u| u.feature).collect()
826+
let missing_features: SmallVec<[_; 1]> = if danger_zone {
827+
needs_check.map(|u| u.into()).collect()
828828
} else {
829829
needs_check
830830
.filter(|u| !is_feature_enabled(u))
831-
.map(|u| u.feature)
831+
.map(|u| u.into())
832832
.collect()
833833
};
834834
if !missing_features.is_empty() {

compiler/rustc_const_eval/src/check_consts/ops.rs

+26-19
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_hir as hir;
88
use rustc_hir::def_id::DefId;
99
use rustc_infer::infer::TyCtxtInferExt;
1010
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
11+
use rustc_middle::middle::stability;
1112
use rustc_middle::mir::CallSource;
1213
use rustc_middle::span_bug;
1314
use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
@@ -305,38 +306,37 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
305306

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

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

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

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

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

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

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

compiler/rustc_const_eval/src/errors.rs

+12-2
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

-2
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,6 @@ hir_analysis_missing_trait_item_suggestion = implement the missing item: `{$snip
309309
310310
hir_analysis_missing_trait_item_unstable = not all trait items implemented, missing: `{$missing_item_name}`
311311
.note = default implementation of `{$missing_item_name}` is unstable
312-
.some_note = use of unstable library feature `{$feature}`: {$reason}
313-
.none_note = use of unstable library feature `{$feature}`
314312
315313
hir_analysis_missing_type_params =
316314
the type {$parameterCount ->

compiler/rustc_hir_analysis/src/check/check.rs

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

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

compiler/rustc_hir_analysis/src/check/mod.rs

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

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

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

compiler/rustc_hir_analysis/src/errors.rs

+6-6
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

+1
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

+6-8
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
@@ -1429,10 +1426,11 @@ impl<'tcx> Pick<'tcx> {
14291426
tcx.disabled_nightly_features(
14301427
lint,
14311428
Some(scope_expr_id),
1432-
self.unstable_candidates.iter().flat_map(|(candidate, features)| {
1433-
features.iter().map(|feature| {
1434-
(format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
1435-
})
1429+
self.unstable_candidates.iter().map(|(candidate, features)| {
1430+
(
1431+
format!(" `{}`", tcx.def_path_str(candidate.item.def_id)),
1432+
features.iter().map(Symbol::as_str).intersperse(", ").collect::<String>(),
1433+
)
14361434
}),
14371435
);
14381436
});

compiler/rustc_lint/src/context/diagnostics.rs

+6-2
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

-10
Original file line numberDiff line numberDiff line change
@@ -2404,16 +2404,6 @@ pub(crate) struct MacroRuleNeverUsed {
24042404
pub name: Symbol,
24052405
}
24062406

2407-
pub(crate) struct UnstableFeature {
2408-
pub msg: DiagMessage,
2409-
}
2410-
2411-
impl<'a> LintDiagnostic<'a, ()> for UnstableFeature {
2412-
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
2413-
diag.primary_message(self.msg);
2414-
}
2415-
}
2416-
24172407
#[derive(LintDiagnostic)]
24182408
#[diag(lint_avoid_intel_syntax)]
24192409
pub(crate) struct AvoidIntelSyntax;

compiler/rustc_lint_defs/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
#![warn(unreachable_pub)]
33
// tidy-alphabetical-end
44

5+
use std::num::NonZero;
6+
57
use rustc_ast::node_id::NodeId;
68
use rustc_ast::{AttrId, Attribute};
79
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
810
use rustc_data_structures::stable_hasher::{
911
HashStable, StableCompare, StableHasher, ToStableHashKey,
1012
};
11-
use rustc_error_messages::{DiagMessage, MultiSpan};
13+
use rustc_error_messages::MultiSpan;
1214
use rustc_hir::def::Namespace;
1315
use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
1416
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

+19
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)