Repository: rust-analyzer/rust-analyzer
Pull request #5957 (2020-09-06 16:28:20)

Add consuming modifier to lvalues that are passed by value and not Copy
Related to #5856 


Patch:
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 25d6f7abd8..d9fc25d88b 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<TextRange> {
     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<SyntaxNode, SyntaxToken>, 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<SyntaxNode, SyntaxToken>,
+    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<RootDatabase>,
     bindings_shadow_count: &mut FxHashMap<Name, u32>,
@@ -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()


Test patch:
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index d0df2e0ec0..cde42024c0 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
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
-        <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
+    <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
+        <span class="value_param">f</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword consuming">self</span><span class="punctuation">)</span>
     <span class="punctuation">}</span>
 
     <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
@@ -80,8 +80,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
-        <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
+    <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
+        <span class="value_param">f</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span>
     <span class="punctuation">}</span>
 
     <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
@@ -144,14 +144,15 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="variable">y</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="unresolved_reference">clone</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-    <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
     <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-    <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 6f72a29bdc..57d4e1252d 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<T> {