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": [