Skip to content

Commit 6c31f6c

Browse files
committed
Provide structured suggestion for #![feature(foo)]
``` error: `S2<'_>` is forbidden as the type of a const generic parameter --> $DIR/lifetime-in-const-param.rs:5:23 | LL | struct S<'a, const N: S2>(&'a ()); | ^^ | = note: the only supported types are integers, `bool` and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] | ``` Fix rust-lang#55941.
1 parent 22e241e commit 6c31f6c

File tree

98 files changed

+755
-253
lines changed

Some content is hidden

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

98 files changed

+755
-253
lines changed

compiler/rustc_const_eval/src/transform/check_consts/ops.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
9999
#[allow(rustc::untranslatable_diagnostic)]
100100
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
101101
let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
102-
let ConstCx { tcx, param_env, .. } = *ccx;
102+
let ConstCx { tcx, param_env, body, .. } = *ccx;
103103

104104
let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
105105
let trait_ref = TraitRef::from_method(tcx, trait_id, args);
@@ -297,10 +297,12 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
297297
ccx.const_kind(),
298298
));
299299

300-
if let Some(feature) = feature
301-
&& ccx.tcx.sess.is_nightly_build()
302-
{
303-
err.help(format!("add `#![feature({feature})]` to the crate attributes to enable",));
300+
if let Some(feature) = feature {
301+
ccx.tcx.disabled_nightly_features(
302+
&mut err,
303+
body.source.def_id().as_local().map(|local| ccx.tcx.local_def_id_to_hir_id(local)),
304+
[(String::new(), feature)],
305+
);
304306
}
305307

306308
if let ConstContext::Static(_) = ccx.const_kind() {

compiler/rustc_hir_analysis/src/astconv/generics.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_middle::ty::{
1616
self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
1717
};
1818
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
19-
use rustc_span::symbol::kw;
19+
use rustc_span::symbol::{kw, sym};
2020
use smallvec::SmallVec;
2121

2222
/// Report an error that a generic argument did not match the generic parameter that was
@@ -41,9 +41,11 @@ fn generic_arg_mismatch_err(
4141
if let GenericParamDefKind::Const { .. } = param.kind {
4242
if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) {
4343
err.help("const arguments cannot yet be inferred with `_`");
44-
if sess.is_nightly_build() {
45-
err.help("add `#![feature(generic_arg_infer)]` to the crate attributes to enable");
46-
}
44+
tcx.disabled_nightly_features(
45+
&mut err,
46+
param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)),
47+
[(String::new(), sym::generic_arg_infer)],
48+
);
4749
}
4850
}
4951

compiler/rustc_hir_analysis/src/check/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,16 @@ fn default_body_is_unstable(
291291
reason: reason_str,
292292
});
293293

294+
let inject_span = item_did
295+
.as_local()
296+
.and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
294297
rustc_session::parse::add_feature_diagnostics_for_issue(
295298
&mut err,
296299
&tcx.sess,
297300
feature,
298301
rustc_feature::GateIssue::Library(issue),
299302
false,
303+
inject_span,
300304
);
301305

302306
err.emit();

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -999,9 +999,14 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
999999
// Implments `ConstParamTy`, suggest adding the feature to enable.
10001000
Ok(..) => true,
10011001
};
1002-
if may_suggest_feature && tcx.sess.is_nightly_build() {
1003-
diag.help(
1004-
"add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
1002+
if may_suggest_feature {
1003+
tcx.disabled_nightly_features(
1004+
&mut diag,
1005+
Some(param.hir_id),
1006+
[(
1007+
" more complex and user defined types".to_string(),
1008+
sym::adt_const_params,
1009+
)],
10051010
);
10061011
}
10071012

compiler/rustc_hir_typeck/src/method/probe.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -1420,15 +1420,13 @@ impl<'tcx> Pick<'tcx> {
14201420
}
14211421
_ => {}
14221422
}
1423-
if tcx.sess.is_nightly_build() {
1424-
for (candidate, feature) in &self.unstable_candidates {
1425-
lint.help(format!(
1426-
"add `#![feature({})]` to the crate attributes to enable `{}`",
1427-
feature,
1428-
tcx.def_path_str(candidate.item.def_id),
1429-
));
1430-
}
1431-
}
1423+
tcx.disabled_nightly_features(
1424+
lint,
1425+
Some(scope_expr_id),
1426+
self.unstable_candidates.iter().map(|(candidate, feature)| {
1427+
(format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
1428+
}),
1429+
);
14321430
},
14331431
);
14341432
}

compiler/rustc_lint/src/levels.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
10781078
feature,
10791079
GateIssue::Language,
10801080
lint_from_cli,
1081+
None,
10811082
);
10821083
},
10831084
);

compiler/rustc_middle/src/ty/context.rs

+42-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal}
4343
#[cfg(parallel_compiler)]
4444
use rustc_data_structures::sync::{DynSend, DynSync};
4545
use rustc_data_structures::unord::UnordSet;
46-
use rustc_errors::{Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan};
46+
use rustc_errors::{
47+
Applicability, Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan,
48+
};
4749
use rustc_hir as hir;
4850
use rustc_hir::def::DefKind;
4951
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
@@ -2174,6 +2176,45 @@ impl<'tcx> TyCtxt<'tcx> {
21742176
lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate);
21752177
}
21762178

2179+
/// Find the crate root and the appropriate span where `use` and outer attributes can be
2180+
/// inserted at.
2181+
pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option<Span> {
2182+
for (_hir_id, node) in self.hir().parent_iter(hir_id) {
2183+
if let hir::Node::Crate(m) = node {
2184+
return Some(m.spans.inject_use_span.shrink_to_lo());
2185+
}
2186+
}
2187+
None
2188+
}
2189+
2190+
pub fn disabled_nightly_features<E: rustc_errors::EmissionGuarantee>(
2191+
self,
2192+
diag: &mut Diag<'_, E>,
2193+
hir_id: Option<HirId>,
2194+
features: impl IntoIterator<Item = (String, Symbol)>,
2195+
) {
2196+
if !self.sess.is_nightly_build() {
2197+
return;
2198+
}
2199+
2200+
let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id));
2201+
for (desc, feature) in features {
2202+
// FIXME: make this string translatable
2203+
let msg =
2204+
format!("add `#![feature({feature})]` to the crate attributes to enable{desc}");
2205+
if let Some(span) = span {
2206+
diag.span_suggestion_verbose(
2207+
span,
2208+
msg,
2209+
format!("#![feature({feature})]\n"),
2210+
Applicability::MachineApplicable,
2211+
);
2212+
} else {
2213+
diag.help(msg);
2214+
}
2215+
}
2216+
}
2217+
21772218
/// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically
21782219
/// generated by `#[derive(LintDiagnostic)]`).
21792220
#[track_caller]

compiler/rustc_passes/src/check_const.rs

+5-10
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,11 @@ impl<'tcx> CheckConstVisitor<'tcx> {
155155
//
156156
// FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
157157
// is a pretty narrow case, however.
158-
if tcx.sess.is_nightly_build() {
159-
for gate in missing_secondary {
160-
// FIXME: make this translatable
161-
#[allow(rustc::diagnostic_outside_of_impl)]
162-
#[allow(rustc::untranslatable_diagnostic)]
163-
err.help(format!(
164-
"add `#![feature({gate})]` to the crate attributes to enable"
165-
));
166-
}
167-
}
158+
tcx.disabled_nightly_features(
159+
&mut err,
160+
def_id.map(|id| tcx.local_def_id_to_hir_id(id)),
161+
missing_secondary.into_iter().map(|gate| (String::new(), *gate)),
162+
);
168163

169164
err.emit();
170165
}

compiler/rustc_session/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ session_feature_diagnostic_for_issue =
2424
session_feature_diagnostic_help =
2525
add `#![feature({$feature})]` to the crate attributes to enable
2626
27+
session_feature_diagnostic_suggestion =
28+
add `#![feature({$feature})]` to the crate attributes to enable
29+
2730
session_feature_suggest_upgrade_compiler =
2831
this compiler was built on {$date}; consider upgrading it if it is out of date
2932

compiler/rustc_session/src/errors.rs

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ pub struct FeatureDiagnosticHelp {
5454
pub feature: Symbol,
5555
}
5656

57+
#[derive(Subdiagnostic)]
58+
#[suggestion(
59+
session_feature_diagnostic_suggestion,
60+
applicability = "maybe-incorrect",
61+
code = "#![feature({feature})]\n"
62+
)]
63+
pub struct FeatureDiagnosticSuggestion {
64+
pub feature: Symbol,
65+
#[primary_span]
66+
pub span: Span,
67+
}
68+
5769
#[derive(Subdiagnostic)]
5870
#[help(session_cli_feature_diagnostic_help)]
5971
pub struct CliFeatureDiagnosticHelp {

compiler/rustc_session/src/parse.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
44
use crate::config::{Cfg, CheckCfg};
55
use crate::errors::{
6-
CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError,
7-
SuggestUpgradeCompiler,
6+
CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp,
7+
FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler,
88
};
99
use crate::lint::{
1010
builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiag, Lint, LintId,
@@ -112,7 +112,7 @@ pub fn feature_err_issue(
112112
}
113113

114114
let mut err = sess.psess.dcx.create_err(FeatureGateError { span, explain: explain.into() });
115-
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false);
115+
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
116116
err
117117
}
118118

@@ -141,7 +141,7 @@ pub fn feature_warn_issue(
141141
explain: &'static str,
142142
) {
143143
let mut err = sess.psess.dcx.struct_span_warn(span, explain);
144-
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false);
144+
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
145145

146146
// Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level
147147
let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
@@ -160,7 +160,7 @@ pub fn add_feature_diagnostics<G: EmissionGuarantee>(
160160
sess: &Session,
161161
feature: Symbol,
162162
) {
163-
add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false);
163+
add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None);
164164
}
165165

166166
/// Adds the diagnostics for a feature to an existing error.
@@ -175,6 +175,7 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
175175
feature: Symbol,
176176
issue: GateIssue,
177177
feature_from_cli: bool,
178+
inject_span: Option<Span>,
178179
) {
179180
if let Some(n) = find_feature_issue(feature, issue) {
180181
err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n });
@@ -184,6 +185,8 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
184185
if sess.psess.unstable_features.is_nightly_build() {
185186
if feature_from_cli {
186187
err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature });
188+
} else if let Some(span) = inject_span {
189+
err.subdiagnostic(sess.dcx(), FeatureDiagnosticSuggestion { feature, span });
187190
} else {
188191
err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature });
189192
}

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -3510,9 +3510,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
35103510
}
35113511
ObligationCauseCode::TrivialBound => {
35123512
err.help("see issue #48214");
3513-
if tcx.sess.opts.unstable_features.is_nightly_build() {
3514-
err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable");
3515-
}
3513+
tcx.disabled_nightly_features(
3514+
err,
3515+
Some(tcx.local_def_id_to_hir_id(body_id)),
3516+
[(String::new(), sym::trivial_bounds)],
3517+
);
35163518
}
35173519
ObligationCauseCode::OpaqueReturnType(expr_info) => {
35183520
if let Some((expr_ty, expr_span)) = expr_info {

src/librustdoc/passes/check_custom_code_classes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
7575
sym::custom_code_classes_in_docs,
7676
GateIssue::Language,
7777
false,
78+
None,
7879
);
7980

8081
err.note(

tests/ui/check-static-values-constraints.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@ LL | field2: SafeEnum::Variant4("str".to_string()),
3636
| ^^^^^^^^^^^
3737
|
3838
= note: calls in statics are limited to constant functions, tuple structs and tuple variants
39-
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
4039
= note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
40+
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
41+
|
42+
LL + #![feature(const_trait_impl)]
43+
|
4144

4245
error[E0010]: allocations are not allowed in statics
4346
--> $DIR/check-static-values-constraints.rs:96:5

tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@
55
// Can never be used as const generics.
66
fn uwu_0<const N: &'static mut ()>() {}
77
//~^ ERROR: forbidden as the type of a const generic
8+
//~| HELP: add `#![feature(adt_const_params)]`
9+
//~| HELP: add `#![feature(adt_const_params)]`
10+
//~| HELP: add `#![feature(adt_const_params)]`
11+
//~| HELP: add `#![feature(adt_const_params)]`
12+
//~| HELP: add `#![feature(adt_const_params)]`
13+
//~| HELP: add `#![feature(adt_const_params)]`
814

915
// Needs the feature but can be used, so suggest adding the feature.
1016
fn owo_0<const N: &'static u32>() {}
1117
//~^ ERROR: forbidden as the type of a const generic
12-
//~^^ HELP: add `#![feature(adt_const_params)]`
1318

1419
// Can only be used in const generics with changes.
1520
struct Meow {
@@ -18,22 +23,17 @@ struct Meow {
1823

1924
fn meow_0<const N: Meow>() {}
2025
//~^ ERROR: forbidden as the type of a const generic
21-
//~^^ HELP: add `#![feature(adt_const_params)]`
2226
fn meow_1<const N: &'static Meow>() {}
2327
//~^ ERROR: forbidden as the type of a const generic
24-
//~^^ HELP: add `#![feature(adt_const_params)]`
2528
fn meow_2<const N: [Meow; 100]>() {}
2629
//~^ ERROR: forbidden as the type of a const generic
27-
//~^^ HELP: add `#![feature(adt_const_params)]`
2830
fn meow_3<const N: (Meow, u8)>() {}
2931
//~^ ERROR: forbidden as the type of a const generic
30-
//~^^ HELP: add `#![feature(adt_const_params)]`
3132

3233
// This is suboptimal that it thinks it can be used
3334
// but better to suggest the feature to the user.
3435
fn meow_4<const N: (Meow, String)>() {}
3536
//~^ ERROR: forbidden as the type of a const generic
36-
//~^^ HELP: add `#![feature(adt_const_params)]`
3737

3838
// Non-local ADT that does not impl `ConstParamTy`
3939
fn nya_0<const N: String>() {}

0 commit comments

Comments
 (0)