diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 25d6f7abd829..d9fc25d88bc8 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::{Local, Name, Semantics, VariantDef}; use ide_db::{ defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, RootDatabase, @@ -13,8 +13,8 @@ use rustc_hash::FxHashMap; use syntax::{ ast::{self, HasFormatSpecifier}, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, - SyntaxKind::*, - TextRange, WalkEvent, T, + SyntaxKind::{self, *}, + SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, }; use crate::FileId; @@ -454,6 +454,32 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option { Some(TextRange::new(range_start, range_end)) } +/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly. +fn parents_match(mut node: NodeOrToken, mut kinds: &[SyntaxKind]) -> bool { + while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) { + if parent.kind() != *kind { + return false; + } + + // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value + // in the same pattern is unstable: rust-lang/rust#68354. + node = node.parent().unwrap().into(); + kinds = rest; + } + + // Only true if we matched all expected kinds + kinds.len() == 0 +} + +fn is_consumed_lvalue( + node: NodeOrToken, + local: &Local, + db: &RootDatabase, +) -> bool { + // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming. + parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db) +} + fn highlight_element( sema: &Semantics, bindings_shadow_count: &mut FxHashMap, @@ -522,6 +548,12 @@ fn highlight_element( let mut h = highlight_def(db, def); + if let Definition::Local(local) = &def { + if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { + h |= HighlightModifier::Consuming; + } + } + if let Some(parent) = name_ref.syntax().parent() { if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { if let Definition::Field(field) = def { @@ -645,21 +677,30 @@ fn highlight_element( .and_then(ast::SelfParam::cast) .and_then(|p| p.mut_token()) .is_some(); - // closure to enforce lazyness - let self_path = || { - sema.resolve_path(&element.parent()?.parent().and_then(ast::Path::cast)?) - }; + let self_path = &element + .parent() + .as_ref() + .and_then(SyntaxNode::parent) + .and_then(ast::Path::cast) + .and_then(|p| sema.resolve_path(&p)); + let mut h = HighlightTag::SelfKeyword.into(); if self_param_is_mut - || matches!(self_path(), + || matches!(self_path, Some(hir::PathResolution::Local(local)) if local.is_self(db) && (local.is_mut(db) || local.ty(db).is_mutable_reference()) ) { - HighlightTag::SelfKeyword | HighlightModifier::Mutable - } else { - HighlightTag::SelfKeyword.into() + h |= HighlightModifier::Mutable } + + if let Some(hir::PathResolution::Local(local)) = self_path { + if is_consumed_lvalue(element, &local, db) { + h |= HighlightModifier::Consuming; + } + } + + h } T![ref] => element .parent() diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index d0df2e0ec04b..cde42024c03d 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html @@ -61,8 +61,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } impl Foo { - fn baz(mut self) -> i32 { - self.x + fn baz(mut self, f: Foo) -> i32 { + f.baz(self) } fn qux(&mut self) { @@ -80,8 +80,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } impl FooCopy { - fn baz(self) -> u32 { - self.x + fn baz(self, f: FooCopy) -> u32 { + f.baz(self) } fn qux(&mut self) { @@ -144,14 +144,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd y; let mut foo = Foo { x, y: x }; + let foo2 = foo.clone(); foo.quop(); foo.qux(); - foo.baz(); + foo.baz(foo2); let mut copy = FooCopy { x }; copy.quop(); copy.qux(); - copy.baz(); + copy.baz(copy); } enum Option<T> { diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 6f72a29bdc49..57d4e1252d28 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -35,8 +35,8 @@ impl Bar for Foo { } impl Foo { - fn baz(mut self) -> i32 { - self.x + fn baz(mut self, f: Foo) -> i32 { + f.baz(self) } fn qux(&mut self) { @@ -54,8 +54,8 @@ struct FooCopy { } impl FooCopy { - fn baz(self) -> u32 { - self.x + fn baz(self, f: FooCopy) -> u32 { + f.baz(self) } fn qux(&mut self) { @@ -118,14 +118,15 @@ fn main() { y; let mut foo = Foo { x, y: x }; + let foo2 = foo.clone(); foo.quop(); foo.qux(); - foo.baz(); + foo.baz(foo2); let mut copy = FooCopy { x }; copy.quop(); copy.qux(); - copy.baz(); + copy.baz(copy); } enum Option {