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