diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index b65be4fe1b2c..8ecf72bd9169 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -31,8 +31,7 @@ use hir_ty::{
autoderef,
display::{HirDisplayError, HirFormatter},
method_resolution,
- traits::Solution,
- traits::SolutionVariables,
+ traits::{FnTrait, Solution, SolutionVariables},
ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate,
InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty,
TyDefId, TyKind, TypeCtor,
@@ -1385,6 +1384,28 @@ impl Type {
)
}
+ /// Checks that particular type `ty` implements `std::ops::FnOnce`.
+ ///
+ /// This function can be used to check if a particular type is callable, since FnOnce is a
+ /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
+ pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
+ let krate = self.krate;
+
+ let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) {
+ Some(it) => it,
+ None => return false,
+ };
+
+ let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
+ method_resolution::implements_trait(
+ &canonical_ty,
+ db,
+ self.ty.environment.clone(),
+ krate,
+ fnonce_trait,
+ )
+ }
+
pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
let trait_ref = hir_ty::TraitRef {
trait_: trait_.id,
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index b35c031627fa..75084846762f 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -763,6 +763,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
if local.is_mut(db) || local.ty(db).is_mutable_reference() {
h |= HighlightModifier::Mutable;
}
+ if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) {
+ h |= HighlightModifier::Callable;
+ }
return h;
}
}
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index c1b817f06cca..e8f78ad525c3 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -64,6 +64,7 @@ pub enum HighlightModifier {
Mutable,
Consuming,
Unsafe,
+ Callable,
}
impl HighlightTag {
@@ -122,6 +123,7 @@ impl HighlightModifier {
HighlightModifier::Mutable,
HighlightModifier::Consuming,
HighlightModifier::Unsafe,
+ HighlightModifier::Callable,
];
fn as_str(self) -> &'static str {
@@ -134,6 +136,7 @@ impl HighlightModifier {
HighlightModifier::Mutable => "mutable",
HighlightModifier::Consuming => "consuming",
HighlightModifier::Unsafe => "unsafe",
+ HighlightModifier::Callable => "callable",
}
}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 0bb0928e44aa..0cb84866d648 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -44,6 +44,17 @@
pub trait Copy {}
}
+pub mod ops {
+ #[lang = "fn_once"]
+ pub trait FnOnce<Args> {}
+
+ #[lang = "fn_mut"]
+ pub trait FnMut<Args>: FnOnce<Args> {}
+
+ #[lang = "fn"]
+ pub trait Fn<Args>: FnMut<Args> {}
+}
+
struct Foo {
pub x: i32,
@@ -99,6 +110,11 @@
foo::<'a, i32>()
}
+use ops::Fn;
+fn baz<F: Fn() -> ()>(f: F) {
+ f()
+}
+
macro_rules! def_fn {
($($tt:tt)*) => {$($tt)*}
}
@@ -157,6 +173,9 @@
copy.quop();
copy.qux();
copy.baz(copy);
+
+ let a = |x| x;
+ let bar = Foo::baz;
}
enum Option<T> {
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 126363b8beb4..da20c300e28b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -18,6 +18,17 @@ pub mod marker {
pub trait Copy {}
}
+pub mod ops {
+ #[lang = "fn_once"]
+ pub trait FnOnce {}
+
+ #[lang = "fn_mut"]
+ pub trait FnMut: FnOnce {}
+
+ #[lang = "fn"]
+ pub trait Fn: FnMut {}
+}
+
struct Foo {
pub x: i32,
@@ -73,6 +84,11 @@ fn foo<'a, T>() -> T {
foo::<'a, i32>()
}
+use ops::Fn;
+fn baz ()>(f: F) {
+ f()
+}
+
macro_rules! def_fn {
($($tt:tt)*) => {$($tt)*}
}
@@ -131,6 +147,9 @@ fn main() {
copy.quop();
copy.qux();
copy.baz(copy);
+
+ let a = |x| x;
+ let bar = Foo::baz;
}
enum Option {
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index a6c4d6099bcd..7df28c9ddcc5 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -77,6 +77,7 @@ define_semantic_token_modifiers![
(CONSUMING, "consuming"),
(UNSAFE, "unsafe"),
(ATTRIBUTE_MODIFIER, "attribute"),
+ (CALLABLE, "callable"),
];
#[derive(Default)]
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index aeacde0f7a2d..1a0b435bfa6b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -408,6 +408,7 @@ fn semantic_token_type_and_modifiers(
HighlightModifier::Mutable => semantic_tokens::MUTABLE,
HighlightModifier::Consuming => semantic_tokens::CONSUMING,
HighlightModifier::Unsafe => semantic_tokens::UNSAFE,
+ HighlightModifier::Callable => semantic_tokens::CALLABLE,
};
mods |= modifier;
}
diff --git a/editors/code/package.json b/editors/code/package.json
index 4bd3117fc806..af845d7bc429 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -929,6 +929,10 @@
{
"id": "consuming",
"description": "Style for non-Copy lvalues consumed by method/function call"
+ },
+ {
+ "id": "callable",
+ "description": "Style for variables/parameters that can be used in call expressions"
}
],
"semanticTokenScopes": [