Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/utils.rs')
| -rw-r--r-- | crates/hir-ty/src/utils.rs | 78 |
1 files changed, 72 insertions, 6 deletions
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index bf7892f69b..c131e97bc4 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -9,19 +9,22 @@ use chalk_ir::{ DebruijnIndex, }; use hir_def::{ + attr::Attrs, db::DefDatabase, generics::{WherePredicate, WherePredicateTypeTarget}, lang_item::LangItem, resolver::{HasResolver, TypeNs}, + tt, type_ref::{TraitBoundModifier, TypeRef}, EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, }; use hir_expand::name::Name; -use intern::sym; +use intern::{sym, Symbol}; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; +use span::Edition; use stdx::never; use crate::{ @@ -264,10 +267,65 @@ impl<'a> ClosureSubst<'a> { } } -pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { +#[derive(Debug, Default)] +pub struct TargetFeatures { + enabled: FxHashSet<Symbol>, +} + +impl TargetFeatures { + pub fn from_attrs(attrs: &Attrs) -> Self { + let enabled = attrs + .by_key(&sym::target_feature) + .tt_values() + .filter_map(|tt| { + match tt.token_trees().flat_tokens() { + [ + tt::TokenTree::Leaf(tt::Leaf::Ident(enable_ident)), + tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. })), + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { kind: tt::LitKind::Str, symbol: features, .. })), + ] if enable_ident.sym == sym::enable => Some(features), + _ => None, + } + }) + .flat_map(|features| features.as_str().split(',').map(Symbol::intern)) + .collect(); + Self { enabled } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Unsafety { + Safe, + Unsafe, + /// A lint. + DeprecatedSafe2024, +} + +pub fn is_fn_unsafe_to_call( + db: &dyn HirDatabase, + func: FunctionId, + caller_target_features: &TargetFeatures, + call_edition: Edition, +) -> Unsafety { let data = db.function_data(func); if data.is_unsafe() { - return true; + return Unsafety::Unsafe; + } + + if data.has_target_feature() { + // RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>. + let callee_target_features = TargetFeatures::from_attrs(&db.attrs(func.into())); + if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) { + return Unsafety::Unsafe; + } + } + + if data.is_deprecated_safe_2024() { + if call_edition.at_least_2024() { + return Unsafety::Unsafe; + } else { + return Unsafety::DeprecatedSafe2024; + } } let loc = func.lookup(db.upcast()); @@ -279,14 +337,22 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { if is_intrinsic_block { // legacy intrinsics // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute - !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() + if db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() { + Unsafety::Safe + } else { + Unsafety::Unsafe + } } else { // Function in an `extern` block are always unsafe to call, except when // it is marked as `safe`. - !data.is_safe() + if data.is_safe() { + Unsafety::Safe + } else { + Unsafety::Unsafe + } } } - _ => false, + _ => Unsafety::Safe, } } |