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 #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

Lines changed: 7 additions & 5 deletions
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

Lines changed: 6 additions & 4 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 8 additions & 3 deletions
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

Lines changed: 7 additions & 9 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 42 additions & 1 deletion
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

Lines changed: 5 additions & 10 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 12 additions & 0 deletions
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 {

0 commit comments

Comments
 (0)