Structured suggestion for "missing feature in unstable fn call"

When encountering a call corresponding to an item marked as unstable behind a feature flag, provide a structured suggestion pointing at where in the crate the `#![feature(..)]` needs to be written.

```
error: `foobar` is not yet stable as a const fn
  --> $DIR/const-stability-attribute-implies-no-feature.rs:12:5
   |
LL |     foobar();
   |     ^^^^^^^^
   |
help: add `#![feature(const_foobar)]` to the crate attributes to enable
   |
LL + #![feature(const_foobar)]
   |
```

Fix #81370.
This commit is contained in:
Esteban Küber 2024-12-28 18:20:44 +00:00
parent bcd0683e5d
commit a47fee50bd
3 changed files with 25 additions and 4 deletions

View file

@ -911,16 +911,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// regular stability. // regular stability.
feature == sym::rustc_private feature == sym::rustc_private
&& issue == NonZero::new(27812) && issue == NonZero::new(27812)
&& self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked && tcx.sess.opts.unstable_opts.force_unstable_if_unmarked
}; };
// Even if the feature is enabled, we still need check_op to double-check // Even if the feature is enabled, we still need check_op to double-check
// this if the callee is not safe to expose on stable. // this if the callee is not safe to expose on stable.
if !feature_enabled || !callee_safe_to_expose_on_stable { if !feature_enabled || !callee_safe_to_expose_on_stable {
let suggestion_span =
tcx.hir_crate_items(()).definitions().next().and_then(|id| {
tcx.crate_level_attribute_injection_span(
tcx.local_def_id_to_hir_id(id),
)
});
self.check_op(ops::FnCallUnstable { self.check_op(ops::FnCallUnstable {
def_id: callee, def_id: callee,
feature, feature,
feature_enabled, feature_enabled,
safe_to_expose_on_stable: callee_safe_to_expose_on_stable, safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
suggestion_span,
}); });
} }
} }

View file

@ -1,8 +1,8 @@
//! Concrete error types for all operations which may be invalid in a certain const context. //! Concrete error types for all operations which may be invalid in a certain const context.
use hir::{ConstContext, LangItem}; use hir::{ConstContext, LangItem};
use rustc_errors::Diag;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
@ -388,6 +388,7 @@ pub(crate) struct FnCallUnstable {
/// expose on stable. /// expose on stable.
pub feature_enabled: bool, pub feature_enabled: bool,
pub safe_to_expose_on_stable: bool, pub safe_to_expose_on_stable: bool,
pub suggestion_span: Option<Span>,
} }
impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
@ -407,8 +408,18 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
def_path: ccx.tcx.def_path_str(self.def_id), def_path: ccx.tcx.def_path_str(self.def_id),
}); });
// FIXME: make this translatable // FIXME: make this translatable
let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature);
#[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::untranslatable_diagnostic)]
err.help(format!("add `#![feature({})]` to the crate attributes to enable", self.feature)); if let Some(span) = self.suggestion_span {
err.span_suggestion_verbose(
span,
msg,
format!("#![feature({})]\n", self.feature),
Applicability::MachineApplicable,
);
} else {
err.help(msg);
}
err err
} }

View file

@ -4,7 +4,10 @@ error: `foobar` is not yet stable as a const fn
LL | foobar(); LL | foobar();
| ^^^^^^^^ | ^^^^^^^^
| |
= help: add `#![feature(const_foobar)]` to the crate attributes to enable help: add `#![feature(const_foobar)]` to the crate attributes to enable
|
LL + #![feature(const_foobar)]
|
error: aborting due to 1 previous error error: aborting due to 1 previous error