Skip to content

Commit

Permalink
Provide structured suggestion for #![feature(foo)]
Browse files Browse the repository at this point in the history
```
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.
  • Loading branch information
estebank committed Mar 18, 2024
1 parent 22e241e commit 6c31f6c
Show file tree
Hide file tree
Showing 98 changed files with 755 additions and 253 deletions.
12 changes: 7 additions & 5 deletions compiler/rustc_const_eval/src/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
#[allow(rustc::untranslatable_diagnostic)]
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
let ConstCx { tcx, param_env, .. } = *ccx;
let ConstCx { tcx, param_env, body, .. } = *ccx;

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

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

if let ConstContext::Static(_) = ccx.const_kind() {
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_hir_analysis/src/astconv/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::ty::{
self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
};
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
use rustc_span::symbol::kw;
use rustc_span::symbol::{kw, sym};
use smallvec::SmallVec;

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

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,16 @@ fn default_body_is_unstable(
reason: reason_str,
});

let inject_span = item_did
.as_local()
.and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
rustc_session::parse::add_feature_diagnostics_for_issue(
&mut err,
&tcx.sess,
feature,
rustc_feature::GateIssue::Library(issue),
false,
inject_span,
);

err.emit();
Expand Down
11 changes: 8 additions & 3 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -999,9 +999,14 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
// Implments `ConstParamTy`, suggest adding the feature to enable.
Ok(..) => true,
};
if may_suggest_feature && tcx.sess.is_nightly_build() {
diag.help(
"add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
if may_suggest_feature {
tcx.disabled_nightly_features(
&mut diag,
Some(param.hir_id),
[(
" more complex and user defined types".to_string(),
sym::adt_const_params,
)],
);
}

Expand Down
16 changes: 7 additions & 9 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1420,15 +1420,13 @@ impl<'tcx> Pick<'tcx> {
}
_ => {}
}
if tcx.sess.is_nightly_build() {
for (candidate, feature) in &self.unstable_candidates {
lint.help(format!(
"add `#![feature({})]` to the crate attributes to enable `{}`",
feature,
tcx.def_path_str(candidate.item.def_id),
));
}
}
tcx.disabled_nightly_features(
lint,
Some(scope_expr_id),
self.unstable_candidates.iter().map(|(candidate, feature)| {
(format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
}),
);
},
);
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/levels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
feature,
GateIssue::Language,
lint_from_cli,
None,
);
},
);
Expand Down
43 changes: 42 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal}
#[cfg(parallel_compiler)]
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_data_structures::unord::UnordSet;
use rustc_errors::{Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan};
use rustc_errors::{
Applicability, Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
Expand Down Expand Up @@ -2174,6 +2176,45 @@ impl<'tcx> TyCtxt<'tcx> {
lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate);
}

/// Find the crate root and the appropriate span where `use` and outer attributes can be
/// inserted at.
pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option<Span> {
for (_hir_id, node) in self.hir().parent_iter(hir_id) {
if let hir::Node::Crate(m) = node {
return Some(m.spans.inject_use_span.shrink_to_lo());
}
}
None
}

pub fn disabled_nightly_features<E: rustc_errors::EmissionGuarantee>(
self,
diag: &mut Diag<'_, E>,
hir_id: Option<HirId>,
features: impl IntoIterator<Item = (String, Symbol)>,
) {
if !self.sess.is_nightly_build() {
return;
}

let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id));
for (desc, feature) in features {
// FIXME: make this string translatable
let msg =
format!("add `#![feature({feature})]` to the crate attributes to enable{desc}");
if let Some(span) = span {
diag.span_suggestion_verbose(
span,
msg,
format!("#![feature({feature})]\n"),
Applicability::MachineApplicable,
);
} else {
diag.help(msg);
}
}
}

/// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically
/// generated by `#[derive(LintDiagnostic)]`).
#[track_caller]
Expand Down
15 changes: 5 additions & 10 deletions compiler/rustc_passes/src/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,11 @@ impl<'tcx> CheckConstVisitor<'tcx> {
//
// FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
// is a pretty narrow case, however.
if tcx.sess.is_nightly_build() {
for gate in missing_secondary {
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
err.help(format!(
"add `#![feature({gate})]` to the crate attributes to enable"
));
}
}
tcx.disabled_nightly_features(
&mut err,
def_id.map(|id| tcx.local_def_id_to_hir_id(id)),
missing_secondary.into_iter().map(|gate| (String::new(), *gate)),
);

err.emit();
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_session/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ session_feature_diagnostic_for_issue =
session_feature_diagnostic_help =
add `#![feature({$feature})]` to the crate attributes to enable
session_feature_diagnostic_suggestion =
add `#![feature({$feature})]` to the crate attributes to enable
session_feature_suggest_upgrade_compiler =
this compiler was built on {$date}; consider upgrading it if it is out of date
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_session/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ pub struct FeatureDiagnosticHelp {
pub feature: Symbol,
}

#[derive(Subdiagnostic)]
#[suggestion(
session_feature_diagnostic_suggestion,
applicability = "maybe-incorrect",
code = "#![feature({feature})]\n"
)]
pub struct FeatureDiagnosticSuggestion {
pub feature: Symbol,
#[primary_span]
pub span: Span,
}

#[derive(Subdiagnostic)]
#[help(session_cli_feature_diagnostic_help)]
pub struct CliFeatureDiagnosticHelp {
Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_session/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use crate::config::{Cfg, CheckCfg};
use crate::errors::{
CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError,
SuggestUpgradeCompiler,
CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp,
FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler,
};
use crate::lint::{
builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiag, Lint, LintId,
Expand Down Expand Up @@ -112,7 +112,7 @@ pub fn feature_err_issue(
}

let mut err = sess.psess.dcx.create_err(FeatureGateError { span, explain: explain.into() });
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false);
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
err
}

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

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

/// Adds the diagnostics for a feature to an existing error.
Expand All @@ -175,6 +175,7 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
feature: Symbol,
issue: GateIssue,
feature_from_cli: bool,
inject_span: Option<Span>,
) {
if let Some(n) = find_feature_issue(feature, issue) {
err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n });
Expand All @@ -184,6 +185,8 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
if sess.psess.unstable_features.is_nightly_build() {
if feature_from_cli {
err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature });
} else if let Some(span) = inject_span {
err.subdiagnostic(sess.dcx(), FeatureDiagnosticSuggestion { feature, span });
} else {
err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3510,9 +3510,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
ObligationCauseCode::TrivialBound => {
err.help("see issue #48214");
if tcx.sess.opts.unstable_features.is_nightly_build() {
err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable");
}
tcx.disabled_nightly_features(
err,
Some(tcx.local_def_id_to_hir_id(body_id)),
[(String::new(), sym::trivial_bounds)],
);
}
ObligationCauseCode::OpaqueReturnType(expr_info) => {
if let Some((expr_ty, expr_span)) = expr_info {
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/passes/check_custom_code_classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
sym::custom_code_classes_in_docs,
GateIssue::Language,
false,
None,
);

err.note(
Expand Down
5 changes: 4 additions & 1 deletion tests/ui/check-static-values-constraints.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ LL | field2: SafeEnum::Variant4("str".to_string()),
| ^^^^^^^^^^^
|
= note: calls in statics are limited to constant functions, tuple structs and tuple variants
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
= note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
LL + #![feature(const_trait_impl)]
|

error[E0010]: allocations are not allowed in statics
--> $DIR/check-static-values-constraints.rs:96:5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
// Can never be used as const generics.
fn uwu_0<const N: &'static mut ()>() {}
//~^ ERROR: forbidden as the type of a const generic
//~| HELP: add `#![feature(adt_const_params)]`
//~| HELP: add `#![feature(adt_const_params)]`
//~| HELP: add `#![feature(adt_const_params)]`
//~| HELP: add `#![feature(adt_const_params)]`
//~| HELP: add `#![feature(adt_const_params)]`
//~| HELP: add `#![feature(adt_const_params)]`

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

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

fn meow_0<const N: Meow>() {}
//~^ ERROR: forbidden as the type of a const generic
//~^^ HELP: add `#![feature(adt_const_params)]`
fn meow_1<const N: &'static Meow>() {}
//~^ ERROR: forbidden as the type of a const generic
//~^^ HELP: add `#![feature(adt_const_params)]`
fn meow_2<const N: [Meow; 100]>() {}
//~^ ERROR: forbidden as the type of a const generic
//~^^ HELP: add `#![feature(adt_const_params)]`
fn meow_3<const N: (Meow, u8)>() {}
//~^ ERROR: forbidden as the type of a const generic
//~^^ HELP: add `#![feature(adt_const_params)]`

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

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

0 comments on commit 6c31f6c

Please sign in to comment.