Skip to content

Commit 2906469

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 3cbb932 commit 2906469

File tree

98 files changed

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

+754
-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_feature(
302+
&mut err,
303+
body.source.def_id().as_local().map(|local| ccx.tcx.local_def_id_to_hir_id(local)),
304+
vec![(String::new(), feature)].into_iter(),
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_feature(
45+
&mut err,
46+
param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)),
47+
vec![(String::new(), sym::generic_arg_infer)].into_iter(),
48+
);
4749
}
4850
}
4951

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,15 @@ fn default_body_is_unstable(
291291
reason: reason_str,
292292
});
293293

294+
let inject_span =
295+
item_did.as_local().and_then(|id| tcx.crate_inject_span(tcx.local_def_id_to_hir_id(id)));
294296
rustc_session::parse::add_feature_diagnostics_for_issue(
295297
&mut err,
296298
&tcx.sess,
297299
feature,
298300
rustc_feature::GateIssue::Library(issue),
299301
false,
302+
inject_span,
300303
);
301304

302305
err.emit();

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -998,9 +998,15 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
998998
// Implments `ConstParamTy`, suggest adding the feature to enable.
999999
Ok(..) => true,
10001000
};
1001-
if may_suggest_feature && tcx.sess.is_nightly_build() {
1002-
diag.help(
1003-
"add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
1001+
if may_suggest_feature {
1002+
tcx.disabled_nightly_feature(
1003+
&mut diag,
1004+
Some(param.hir_id),
1005+
vec![(
1006+
" more complex and user defined types".to_string(),
1007+
sym::adt_const_params,
1008+
)]
1009+
.into_iter(),
10041010
);
10051011
}
10061012

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_feature(
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
@@ -1082,6 +1082,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
10821082
feature,
10831083
GateIssue::Language,
10841084
lint_from_cli,
1085+
None,
10851086
);
10861087
},
10871088
);

compiler/rustc_middle/src/ty/context.rs

Lines changed: 41 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};
@@ -2171,6 +2173,44 @@ impl<'tcx> TyCtxt<'tcx> {
21712173
lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate);
21722174
}
21732175

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