From 7009d5ee2bff53b9beb555b1572c97ab3882cd98 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 30 Jul 2020 10:20:06 -0400 Subject: Add new HighlightModifier variant, Consuming --- crates/ide/src/syntax_highlighting/tags.rs | 3 +++ crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 1 + 3 files changed, 5 insertions(+) (limited to 'crates') diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 49ec94bdc..c1b817f06 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -62,6 +62,7 @@ pub enum HighlightModifier { Documentation, Injected, Mutable, + Consuming, Unsafe, } @@ -119,6 +120,7 @@ impl HighlightModifier { HighlightModifier::Documentation, HighlightModifier::Injected, HighlightModifier::Mutable, + HighlightModifier::Consuming, HighlightModifier::Unsafe, ]; @@ -130,6 +132,7 @@ impl HighlightModifier { HighlightModifier::Documentation => "documentation", HighlightModifier::Injected => "injected", HighlightModifier::Mutable => "mutable", + HighlightModifier::Consuming => "consuming", HighlightModifier::Unsafe => "unsafe", } } diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index afc38fb4e..9db7b8af5 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -75,6 +75,7 @@ define_semantic_token_modifiers![ (CONTROL_FLOW, "controlFlow"), (INJECTED, "injected"), (MUTABLE, "mutable"), + (CONSUMING, "consuming"), (UNSAFE, "unsafe"), (ATTRIBUTE_MODIFIER, "attribute"), ]; diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 8a2cfa2ae..44c006e4c 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -400,6 +400,7 @@ fn semantic_token_type_and_modifiers( HighlightModifier::Injected => semantic_tokens::INJECTED, HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, HighlightModifier::Mutable => semantic_tokens::MUTABLE, + HighlightModifier::Consuming => semantic_tokens::CONSUMING, HighlightModifier::Unsafe => semantic_tokens::UNSAFE, }; mods |= modifier; -- cgit v1.2.3 From a044ff0138d6bff9406b94de89fde43e7672ee1b Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 30 Jul 2020 11:07:13 -0400 Subject: Mark mutating functions with `mutable` modifier, and owning functions with `consuming`. --- crates/hir/src/code_model.rs | 15 +++++++++++ crates/hir/src/semantics.rs | 4 +-- crates/ide/src/syntax_highlighting.rs | 42 ++++++++++++++++++++++------- crates/ide/src/syntax_highlighting/tests.rs | 9 +++++++ crates/ide/test_data/highlighting.html | 15 ++++++++--- 5 files changed, 70 insertions(+), 15 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index c442654dd..31d5276b0 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs @@ -670,6 +670,21 @@ impl Function { db.function_data(self.id).has_self_param } + pub fn mutability_of_self_param(self, db: &dyn HirDatabase) -> Option { + let func_data = db.function_data(self.id); + if !func_data.has_self_param { + return None; + } + + func_data.params.first().and_then(|param| { + if let TypeRef::Reference(_, mutability) = param { + Some(*mutability) + } else { + None + } + }) + } + pub fn params(self, db: &dyn HirDatabase) -> Vec { db.function_data(self.id).params.clone() } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index c693176fa..9f23315c3 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -267,7 +267,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.assert_contains_node(node) } - pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { + pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool { self.imp.is_unsafe_method_call(method_call_expr) } @@ -571,7 +571,7 @@ impl<'db> SemanticsImpl<'db> { InFile::new(file_id, node) } - pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { + pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool { method_call_expr .expr() .and_then(|expr| { diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 5d7c7e8d0..15a78a614 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -4,7 +4,7 @@ mod injection; #[cfg(test)] mod tests; -use hir::{Name, Semantics, VariantDef}; +use hir::{Mutability, Name, Semantics, VariantDef}; use ide_db::{ defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, RootDatabase, @@ -729,13 +729,23 @@ fn highlight_name( let is_unsafe = name_ref .and_then(|name_ref| name_ref.syntax().parent()) .and_then(ast::MethodCallExpr::cast) - .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr)) + .map(|method_call_expr| sema.is_unsafe_method_call(&method_call_expr)) .unwrap_or(false); if is_unsafe { h |= HighlightModifier::Unsafe; } } - return h; + return if func.has_self_param(db) { + match func.mutability_of_self_param(db) { + Some(mutability) => match mutability { + Mutability::Mut => h | HighlightModifier::Mutable, + Mutability::Shared => h, + }, + None => h | HighlightModifier::Consuming, + } + } else { + h + }; } hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, @@ -808,14 +818,26 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics { let mut h = Highlight::new(HighlightTag::Function); - let is_unsafe = ast::MethodCallExpr::cast(parent) - .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr)) - .unwrap_or(false); - if is_unsafe { - h |= HighlightModifier::Unsafe; - } + ast::MethodCallExpr::cast(parent) + .and_then(|method_call_expr| { + if sema.is_unsafe_method_call(&method_call_expr) { + h |= HighlightModifier::Unsafe; + } + + let func = sema.resolve_method_call(&method_call_expr)?; + if !func.has_self_param(sema.db) { + return Some(h); + } - h + Some(match func.mutability_of_self_param(sema.db) { + Some(mutability) => match mutability { + Mutability::Mut => h | HighlightModifier::Mutable, + Mutability::Shared => h, + }, + None => h | HighlightModifier::Consuming, + }) + }) + .unwrap_or_else(|| h) } FIELD_EXPR => { let h = HighlightTag::Field; diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 94f37d773..6cb955d29 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -36,6 +36,10 @@ impl Foo { fn qux(&mut self) { self.x = 0; } + + fn quop(&self) -> i32 { + self.x + } } static mut STATIC_MUT: i32 = 0; @@ -87,6 +91,11 @@ fn main() { let Foo { x: z, y } = Foo { x: z, y }; y; + + let mut foo = Foo { x, y: x }; + foo.quop(); + foo.qux(); + foo.baz(); } enum Option { diff --git a/crates/ide/test_data/highlighting.html b/crates/ide/test_data/highlighting.html index 8e0160eee..2aad06a92 100644 --- a/crates/ide/test_data/highlighting.html +++ b/crates/ide/test_data/highlighting.html @@ -55,13 +55,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } impl Foo { - fn baz(mut self) -> i32 { + fn baz(mut self) -> i32 { self.x } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } + + fn quop(&self) -> i32 { + self.x + } } static mut STATIC_MUT: i32 = 0; @@ -113,6 +117,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let Foo { x: z, y } = Foo { x: z, y }; y; + + let mut foo = Foo { x, y: x }; + foo.quop(); + foo.qux(); + foo.baz(); } enum Option<T> { @@ -122,7 +131,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd use Option::*; impl<T> Option<T> { - fn and<U>(self, other: Option<U>) -> Option<(T, U)> { + fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { None => unimplemented!(), Nope => Nope, -- cgit v1.2.3 From 3456e2eec7c1e18734f8fa41924a83b4c676dc00 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 30 Jul 2020 22:31:53 -0400 Subject: Add new method to Semantics, method_receiver_kind, which returns the kind of self The options are Shared, Mutable, Consuming, and Copied. Use this to add proper highlighting to methods based on usage. --- crates/hir/src/lib.rs | 2 +- crates/hir/src/semantics.rs | 46 ++++++++- crates/ide/src/syntax_highlighting.rs | 139 +++++++++++++++------------- crates/ide/src/syntax_highlighting/tests.rs | 30 ++++++ crates/ide/test_data/highlighting.html | 34 ++++++- 5 files changed, 181 insertions(+), 70 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8961ba8fd..fc1c1ccd3 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -38,7 +38,7 @@ pub use crate::{ Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, }, has_source::HasSource, - semantics::{original_range, PathResolution, Semantics, SemanticsScope}, + semantics::{original_range, PathResolution, SelfKind, Semantics, SemanticsScope}, }; pub use hir_def::{ diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 9f23315c3..aff0e73da 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -6,8 +6,10 @@ use std::{cell::RefCell, fmt, iter::successors}; use base_db::{FileId, FileRange}; use hir_def::{ + lang_item::LangItemTarget, resolver::{self, HasResolver, Resolver, TypeNs}, - AsMacroCall, FunctionId, TraitId, VariantId, + src::HasSource, + AsMacroCall, FunctionId, Lookup, TraitId, VariantId, }; use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; use hir_ty::associated_type_shorthand_candidates; @@ -15,7 +17,7 @@ use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ algo::{find_node_at_offset, skip_trivia_token}, - ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize, + ast, AstNode, Direction, SmolStr, SyntaxNode, SyntaxToken, TextRange, TextSize, }; use crate::{ @@ -79,6 +81,13 @@ impl PathResolution { } } +pub enum SelfKind { + Shared, + Mutable, + Consuming, + Copied, +} + /// Primary API to get semantic information, like types, from syntax trees. pub struct Semantics<'db, DB> { pub db: &'db DB, @@ -188,6 +197,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.type_of_self(param) } + pub fn method_reciever_kind(&self, call: &ast::MethodCallExpr) -> Option { + self.imp.method_receiver_kind(call) + } + pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { self.imp.resolve_method_call(call).map(Function::from) } @@ -410,6 +423,35 @@ impl<'db> SemanticsImpl<'db> { self.analyze(param.syntax()).type_of_self(self.db, ¶m) } + fn method_receiver_kind(&self, call: &ast::MethodCallExpr) -> Option { + self.resolve_method_call(call).and_then(|func| { + let lookup = func.lookup(self.db.upcast()); + let src = lookup.source(self.db.upcast()); + let param_list = src.value.param_list()?; + let self_param = param_list.self_param()?; + if self_param.amp_token().is_some() { + return Some(if self_param.mut_token().is_some() { + SelfKind::Mutable + } else { + SelfKind::Shared + }); + } + + let ty = self.type_of_expr(&call.expr()?)?; + let krate = Function::from(func).krate(self.db)?; + let lang_item = self.db.lang_item(krate.id, SmolStr::new("copy")); + let copy_trait = match lang_item? { + LangItemTarget::TraitId(copy_trait) => Trait::from(copy_trait), + _ => return None, + }; + Some(if ty.impls_trait(self.db, copy_trait, &[]) { + SelfKind::Copied + } else { + SelfKind::Consuming + }) + }) + } + fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax()).resolve_method_call(self.db, call) } diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 15a78a614..9827c68af 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -4,7 +4,7 @@ mod injection; #[cfg(test)] mod tests; -use hir::{Mutability, Name, Semantics, VariantDef}; +use hir::{Mutability, Name, SelfKind, Semantics, VariantDef}; use ide_db::{ defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, RootDatabase, @@ -519,27 +519,29 @@ fn highlight_element( } NAME_REF => { let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); - let possibly_unsafe = is_possibly_unsafe(&name_ref); - match classify_name_ref(sema, &name_ref) { - Some(name_kind) => match name_kind { - NameRefClass::ExternCrate(_) => HighlightTag::Module.into(), - NameRefClass::Definition(def) => { - if let Definition::Local(local) = &def { - if let Some(name) = local.name(db) { - let shadow_count = - bindings_shadow_count.entry(name.clone()).or_default(); - binding_hash = Some(calc_binding_hash(&name, *shadow_count)) - } - }; - highlight_name(sema, db, def, Some(name_ref), possibly_unsafe) + highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| { + let possibly_unsafe = is_possibly_unsafe(&name_ref); + match classify_name_ref(sema, &name_ref) { + Some(name_kind) => match name_kind { + NameRefClass::ExternCrate(_) => HighlightTag::Module.into(), + NameRefClass::Definition(def) => { + if let Definition::Local(local) = &def { + if let Some(name) = local.name(db) { + let shadow_count = + bindings_shadow_count.entry(name.clone()).or_default(); + binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + } + }; + highlight_name(sema, db, def, Some(name_ref), possibly_unsafe) + } + NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), + }, + None if syntactic_name_ref_highlighting => { + highlight_name_ref_by_syntax(name_ref, sema) } - NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), - }, - None if syntactic_name_ref_highlighting => { - highlight_name_ref_by_syntax(name_ref, sema) + None => HighlightTag::UnresolvedReference.into(), } - None => HighlightTag::UnresolvedReference.into(), - } + }) } // Simple token-based highlighting @@ -700,6 +702,35 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool { } } +fn highlight_func_by_name_ref( + sema: &Semantics, + name_ref: &ast::NameRef, +) -> Option { + let parent = name_ref.syntax().parent()?; + let method_call = ast::MethodCallExpr::cast(parent)?; + highlight_method_call(sema, &method_call) +} + +fn highlight_method_call( + sema: &Semantics, + method_call: &ast::MethodCallExpr, +) -> Option { + let func = sema.resolve_method_call(&method_call)?; + let mut h = HighlightTag::Function.into(); + if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { + h |= HighlightModifier::Unsafe; + } + + sema.method_reciever_kind(&method_call) + .map(|self_kind| match self_kind { + SelfKind::Shared => h, + SelfKind::Mutable => h | HighlightModifier::Mutable, + SelfKind::Consuming => h | HighlightModifier::Consuming, + SelfKind::Copied => h, + }) + .or_else(|| Some(h)) +} + fn highlight_name( sema: &Semantics, db: &RootDatabase, @@ -722,30 +753,26 @@ fn highlight_name( Definition::ModuleDef(def) => match def { hir::ModuleDef::Module(_) => HighlightTag::Module, hir::ModuleDef::Function(func) => { - let mut h = HighlightTag::Function.into(); - if func.is_unsafe(db) { - h |= HighlightModifier::Unsafe; - } else { - let is_unsafe = name_ref - .and_then(|name_ref| name_ref.syntax().parent()) - .and_then(ast::MethodCallExpr::cast) - .map(|method_call_expr| sema.is_unsafe_method_call(&method_call_expr)) - .unwrap_or(false); - if is_unsafe { - h |= HighlightModifier::Unsafe; - } - } - return if func.has_self_param(db) { - match func.mutability_of_self_param(db) { - Some(mutability) => match mutability { - Mutability::Mut => h | HighlightModifier::Mutable, - Mutability::Shared => h, - }, - None => h | HighlightModifier::Consuming, - } - } else { - h - }; + return name_ref + .and_then(|name_ref| highlight_func_by_name_ref(sema, &name_ref)) + .unwrap_or_else(|| { + let mut h = HighlightTag::Function.into(); + if func.is_unsafe(db) { + h |= HighlightModifier::Unsafe; + } + + return if func.has_self_param(db) { + match func.mutability_of_self_param(db) { + Some(mutability) => match mutability { + Mutability::Mut => h | HighlightModifier::Mutable, + Mutability::Shared => h, + }, + None => h, + } + } else { + h + }; + }); } hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, @@ -817,27 +844,9 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics { - let mut h = Highlight::new(HighlightTag::Function); - ast::MethodCallExpr::cast(parent) - .and_then(|method_call_expr| { - if sema.is_unsafe_method_call(&method_call_expr) { - h |= HighlightModifier::Unsafe; - } - - let func = sema.resolve_method_call(&method_call_expr)?; - if !func.has_self_param(sema.db) { - return Some(h); - } - - Some(match func.mutability_of_self_param(sema.db) { - Some(mutability) => match mutability { - Mutability::Mut => h | HighlightModifier::Mutable, - Mutability::Shared => h, - }, - None => h | HighlightModifier::Consuming, - }) - }) - .unwrap_or_else(|| h) + return ast::MethodCallExpr::cast(parent) + .and_then(|method_call| highlight_method_call(sema, &method_call)) + .unwrap_or_else(|| HighlightTag::Function.into()); } FIELD_EXPR => { let h = HighlightTag::Field; diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 6cb955d29..ccb76f552 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -12,6 +12,12 @@ fn test_highlighting() { use inner::{self as inner_mod}; mod inner {} +// Needed for function consuming vs normal +pub mod marker { + #[lang = "copy"] + pub trait Copy {} +} + #[derive(Clone, Debug)] struct Foo { pub x: i32, @@ -42,6 +48,25 @@ impl Foo { } } +#[derive(Copy, Clone)] +struct FooCopy { + x: u32, +} + +impl FooCopy { + fn baz(self) -> u32 { + self.x + } + + fn qux(&mut self) { + self.x = 0; + } + + fn quop(&self) -> u32 { + self.x + } +} + static mut STATIC_MUT: i32 = 0; fn foo<'a, T>() -> T { @@ -96,6 +121,11 @@ fn main() { foo.quop(); foo.qux(); foo.baz(); + + let mut copy = FooCopy { x }; + copy.quop(); + copy.qux(); + copy.baz(); } enum Option { diff --git a/crates/ide/test_data/highlighting.html b/crates/ide/test_data/highlighting.html index 2aad06a92..a6b79589b 100644 --- a/crates/ide/test_data/highlighting.html +++ b/crates/ide/test_data/highlighting.html @@ -38,6 +38,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
use inner::{self as inner_mod};
 mod inner {}
 
+// Needed for function consuming vs normal
+pub mod marker {
+    #[lang = "copy"]
+    pub trait Copy {}
+}
+
 #[derive(Clone, Debug)]
 struct Foo {
     pub x: i32,
@@ -55,7 +61,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 impl Foo {
-    fn baz(mut self) -> i32 {
+    fn baz(mut self) -> i32 {
         self.x
     }
 
@@ -68,6 +74,25 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     }
 }
 
+#[derive(Copy, Clone)]
+struct FooCopy {
+    x: u32,
+}
+
+impl FooCopy {
+    fn baz(self) -> u32 {
+        self.x
+    }
+
+    fn qux(&mut self) {
+        self.x = 0;
+    }
+
+    fn quop(&self) -> u32 {
+        self.x
+    }
+}
+
 static mut STATIC_MUT: i32 = 0;
 
 fn foo<'a, T>() -> T {
@@ -122,6 +147,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     foo.quop();
     foo.qux();
     foo.baz();
+
+    let mut copy = FooCopy { x };
+    copy.quop();
+    copy.qux();
+    copy.baz();
 }
 
 enum Option<T> {
@@ -131,7 +161,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 use Option::*;
 
 impl<T> Option<T> {
-    fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
+    fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
         match other {
             None => unimplemented!(),
             Nope => Nope,
-- 
cgit v1.2.3