@@ -4,7 +4,7 @@ mod injection;
4
4
#[ cfg( test) ]
5
5
mod tests;
6
6
7
- use hir:: { Name , Semantics , VariantDef } ;
7
+ use hir:: { Local , Name , Semantics , VariantDef } ;
8
8
use ide_db:: {
9
9
defs:: { classify_name, classify_name_ref, Definition , NameClass , NameRefClass } ,
10
10
RootDatabase ,
@@ -13,8 +13,8 @@ use rustc_hash::FxHashMap;
13
13
use syntax:: {
14
14
ast:: { self , HasFormatSpecifier } ,
15
15
AstNode , AstToken , Direction , NodeOrToken , SyntaxElement ,
16
- SyntaxKind :: * ,
17
- TextRange , WalkEvent , T ,
16
+ SyntaxKind :: { self , * } ,
17
+ SyntaxNode , SyntaxToken , TextRange , WalkEvent , T ,
18
18
} ;
19
19
20
20
use crate :: FileId ;
@@ -454,6 +454,32 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
454
454
Some ( TextRange :: new ( range_start, range_end) )
455
455
}
456
456
457
+ /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
458
+ fn parents_match ( mut node : NodeOrToken < SyntaxNode , SyntaxToken > , mut kinds : & [ SyntaxKind ] ) -> bool {
459
+ while let ( Some ( parent) , [ kind, rest @ ..] ) = ( & node. parent ( ) , kinds) {
460
+ if parent. kind ( ) != * kind {
461
+ return false ;
462
+ }
463
+
464
+ // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
465
+ // in the same pattern is unstable: rust-lang/rust#68354.
466
+ node = node. parent ( ) . unwrap ( ) . into ( ) ;
467
+ kinds = rest;
468
+ }
469
+
470
+ // Only true if we matched all expected kinds
471
+ kinds. len ( ) == 0
472
+ }
473
+
474
+ fn is_consumed_lvalue (
475
+ node : NodeOrToken < SyntaxNode , SyntaxToken > ,
476
+ local : & Local ,
477
+ db : & RootDatabase ,
478
+ ) -> bool {
479
+ // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
480
+ parents_match ( node, & [ PATH_SEGMENT , PATH , PATH_EXPR , ARG_LIST ] ) && !local. ty ( db) . is_copy ( db)
481
+ }
482
+
457
483
fn highlight_element (
458
484
sema : & Semantics < RootDatabase > ,
459
485
bindings_shadow_count : & mut FxHashMap < Name , u32 > ,
@@ -522,6 +548,12 @@ fn highlight_element(
522
548
523
549
let mut h = highlight_def ( db, def) ;
524
550
551
+ if let Definition :: Local ( local) = & def {
552
+ if is_consumed_lvalue ( name_ref. syntax ( ) . clone ( ) . into ( ) , local, db) {
553
+ h |= HighlightModifier :: Consuming ;
554
+ }
555
+ }
556
+
525
557
if let Some ( parent) = name_ref. syntax ( ) . parent ( ) {
526
558
if matches ! ( parent. kind( ) , FIELD_EXPR | RECORD_PAT_FIELD ) {
527
559
if let Definition :: Field ( field) = def {
@@ -645,21 +677,30 @@ fn highlight_element(
645
677
. and_then ( ast:: SelfParam :: cast)
646
678
. and_then ( |p| p. mut_token ( ) )
647
679
. is_some ( ) ;
648
- // closure to enforce lazyness
649
- let self_path = || {
650
- sema. resolve_path ( & element. parent ( ) ?. parent ( ) . and_then ( ast:: Path :: cast) ?)
651
- } ;
680
+ let self_path = & element
681
+ . parent ( )
682
+ . as_ref ( )
683
+ . and_then ( SyntaxNode :: parent)
684
+ . and_then ( ast:: Path :: cast)
685
+ . and_then ( |p| sema. resolve_path ( & p) ) ;
686
+ let mut h = HighlightTag :: SelfKeyword . into ( ) ;
652
687
if self_param_is_mut
653
- || matches ! ( self_path( ) ,
688
+ || matches ! ( self_path,
654
689
Some ( hir:: PathResolution :: Local ( local) )
655
690
if local. is_self( db)
656
691
&& ( local. is_mut( db) || local. ty( db) . is_mutable_reference( ) )
657
692
)
658
693
{
659
- HighlightTag :: SelfKeyword | HighlightModifier :: Mutable
660
- } else {
661
- HighlightTag :: SelfKeyword . into ( )
694
+ h |= HighlightModifier :: Mutable
662
695
}
696
+
697
+ if let Some ( hir:: PathResolution :: Local ( local) ) = self_path {
698
+ if is_consumed_lvalue ( element, & local, db) {
699
+ h |= HighlightModifier :: Consuming ;
700
+ }
701
+ }
702
+
703
+ h
663
704
}
664
705
T ! [ ref] => element
665
706
. parent ( )
0 commit comments