Skip to content

Commit

Permalink
Auto merge of rust-lang#17268 - Veykril:signatures, r=Veykril
Browse files Browse the repository at this point in the history
feat: More callable info

With this PR we retain more info about callables other than functions, allowing for closure parameter type inlay hints to be linkable as well as better signature help around closures and `Fn*` implementors.
  • Loading branch information
bors committed May 22, 2024
2 parents 9f4b651 + 6438554 commit 24bf53d
Show file tree
Hide file tree
Showing 17 changed files with 388 additions and 226 deletions.
3 changes: 0 additions & 3 deletions src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ use std::sync::OnceLock;

use rustc_hash::FxHashMap;

/// Ignored attribute namespaces used by tools.
pub const TOOL_MODULES: &[&str] = &["rustfmt", "clippy"];

pub struct BuiltinAttribute {
pub name: &'static str,
pub template: AttributeTemplate,
Expand Down
10 changes: 9 additions & 1 deletion src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ use crate::{
LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
};

const PREDEFINED_TOOLS: &[SmolStr] = &[
SmolStr::new_static("clippy"),
SmolStr::new_static("rustfmt"),
SmolStr::new_static("diagnostic"),
SmolStr::new_static("miri"),
SmolStr::new_static("rust_analyzer"),
];

/// Contains the results of (early) name resolution.
///
/// A `DefMap` stores the module tree and the definitions that are in scope in every module after
Expand Down Expand Up @@ -160,7 +168,7 @@ impl DefMapCrateData {
fn_proc_macro_mapping: FxHashMap::default(),
proc_macro_loading_error: None,
registered_attrs: Vec::new(),
registered_tools: Vec::new(),
registered_tools: PREDEFINED_TOOLS.into(),
unstable_features: FxHashSet::default(),
rustc_coherence_is_core: false,
no_core: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use syntax::{ast, SmolStr};
use triomphe::Arc;

use crate::{
attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
attr::builtin::find_builtin_attr_idx,
db::DefDatabase,
item_scope::BuiltinShadowMode,
nameres::path_resolution::ResolveMode,
Expand Down Expand Up @@ -82,8 +82,7 @@ impl DefMap {
let name = name.to_smol_str();
let pred = |n: &_| *n == name;

let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
let is_tool = self.data.registered_tools.iter().map(SmolStr::as_str).any(pred);
// FIXME: tool modules can be shadowed by actual modules
if is_tool {
return true;
Expand Down
21 changes: 12 additions & 9 deletions src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,19 +797,22 @@ impl<'a> InferenceTable<'a> {
})
.build();

let projection = {
let b = TyBuilder::subst_for_def(self.db, fn_once_trait, None);
if b.remaining() != 2 {
return None;
}
let fn_once_subst = b.push(ty.clone()).push(arg_ty).build();
let b = TyBuilder::trait_ref(self.db, fn_once_trait);
if b.remaining() != 2 {
return None;
}
let mut trait_ref = b.push(ty.clone()).push(arg_ty).build();

TyBuilder::assoc_type_projection(self.db, output_assoc_type, Some(fn_once_subst))
.build()
let projection = {
TyBuilder::assoc_type_projection(
self.db,
output_assoc_type,
Some(trait_ref.substitution.clone()),
)
.build()
};

let trait_env = self.trait_env.env.clone();
let mut trait_ref = projection.trait_ref(self.db);
let obligation = InEnvironment {
goal: trait_ref.clone().cast(Interner),
environment: trait_env.clone(),
Expand Down
73 changes: 53 additions & 20 deletions src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,10 @@ impl CallableSig {
}
}

pub fn abi(&self) -> FnAbi {
self.abi
}

pub fn params(&self) -> &[Ty] {
&self.params_and_return[0..self.params_and_return.len() - 1]
}
Expand Down Expand Up @@ -892,20 +896,16 @@ where
Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
}

pub fn callable_sig_from_fnonce(
mut self_ty: &Ty,
env: Arc<TraitEnvironment>,
pub fn callable_sig_from_fn_trait(
self_ty: &Ty,
trait_env: Arc<TraitEnvironment>,
db: &dyn HirDatabase,
) -> Option<CallableSig> {
if let Some((ty, _, _)) = self_ty.as_reference() {
// This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
self_ty = ty;
}
let krate = env.krate;
) -> Option<(FnTrait, CallableSig)> {
let krate = trait_env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;

let mut table = InferenceTable::new(db, env);
let mut table = InferenceTable::new(db, trait_env.clone());
let b = TyBuilder::trait_ref(db, fn_once_trait);
if b.remaining() != 2 {
return None;
Expand All @@ -915,23 +915,56 @@ pub fn callable_sig_from_fnonce(
// - Self: FnOnce<?args_ty>
// - <Self as FnOnce<?args_ty>>::Output == ?ret_ty
let args_ty = table.new_type_var();
let trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build();
let mut trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build();
let projection = TyBuilder::assoc_type_projection(
db,
output_assoc_type,
Some(trait_ref.substitution.clone()),
)
.build();
table.register_obligation(trait_ref.cast(Interner));
let ret_ty = table.normalize_projection_ty(projection);

let ret_ty = table.resolve_completely(ret_ty);
let args_ty = table.resolve_completely(args_ty);

let params =
args_ty.as_tuple()?.iter(Interner).map(|it| it.assert_ty_ref(Interner)).cloned().collect();

Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe, FnAbi::RustCall))
let block = trait_env.block;
let trait_env = trait_env.env.clone();
let obligation =
InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() };
let canonical = table.canonicalize(obligation.clone());
if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() {
table.register_obligation(obligation.goal);
let return_ty = table.normalize_projection_ty(projection);
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
let fn_x_trait = fn_x.get_id(db, krate)?;
trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
goal: trait_ref.clone().cast(Interner),
environment: trait_env.clone(),
};
let canonical = table.canonicalize(obligation.clone());
if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() {
let ret_ty = table.resolve_completely(return_ty);
let args_ty = table.resolve_completely(args_ty);
let params = args_ty
.as_tuple()?
.iter(Interner)
.map(|it| it.assert_ty_ref(Interner))
.cloned()
.collect();

return Some((
fn_x,
CallableSig::from_params_and_return(
params,
ret_ty,
false,
Safety::Safe,
FnAbi::RustCall,
),
));
}
}
unreachable!("It should at least implement FnOnce at this point");
} else {
None
}
}

struct PlaceholderCollector<'db> {
Expand Down
19 changes: 19 additions & 0 deletions src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Trait solving using Chalk.

use core::fmt;
use std::env::var;

use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData};
Expand Down Expand Up @@ -209,7 +210,25 @@ pub enum FnTrait {
Fn,
}

impl fmt::Display for FnTrait {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FnTrait::FnOnce => write!(f, "FnOnce"),
FnTrait::FnMut => write!(f, "FnMut"),
FnTrait::Fn => write!(f, "Fn"),
}
}
}

impl FnTrait {
pub const fn function_name(&self) -> &'static str {
match self {
FnTrait::FnOnce => "call_once",
FnTrait::FnMut => "call_mut",
FnTrait::Fn => "call",
}
}

const fn lang_item(self) -> LangItem {
match self {
FnTrait::FnOnce => LangItem::FnOnce,
Expand Down
Loading

0 comments on commit 24bf53d

Please sign in to comment.