@@ -11,8 +11,9 @@ use vfs::FileId;
1111use crate :: {
1212 db:: root_db:: RootDb ,
1313 source_targets:: {
14- SourceTarget , SourceTargetBlock , SourceTargetBlockReason , SourceTargetDomain ,
15- SourceTargetResolution , source_target_at_offset,
14+ SourceTarget , SourceTargetAlternatives , SourceTargetAmbiguity , SourceTargetBlock ,
15+ SourceTargetBlockReason , SourceTargetDomain , SourceTargetResolution ,
16+ source_target_at_offset,
1617 } ,
1718} ;
1819
@@ -51,30 +52,30 @@ bitflags::bitflags! {
5152#[ derive( Debug , Clone ) ]
5253pub ( crate ) enum TargetResolution < ' tree > {
5354 Resolved ( TargetCandidate < ' tree > ) ,
54- Ambiguous ( TargetAmbiguity ) ,
55+ Ambiguous ( TargetAlternatives < ' tree > ) ,
5556 Blocked ( TargetBlock ) ,
5657 Unresolved ,
5758}
5859
5960impl < ' tree > TargetResolution < ' tree > {
60- pub ( crate ) fn for_intent ( self , intent : TargetIntent ) -> Option < SemanticTarget < ' tree > > {
61- self . into_primary ( intent. capability ( ) )
61+ pub ( crate ) fn unique_for_intent ( self , intent : TargetIntent ) -> Option < SemanticTarget < ' tree > > {
62+ let mut targets = self . targets_for_intent ( intent) ;
63+ ( targets. len ( ) == 1 ) . then ( || targets. pop ( ) . expect ( "single target should exist" ) )
6264 }
6365
64- fn into_primary ( self , required : TargetCapability ) -> Option < SemanticTarget < ' tree > > {
66+ pub ( crate ) fn targets_for_intent ( self , intent : TargetIntent ) -> Vec < SemanticTarget < ' tree > > {
67+ let required = intent. capability ( ) ;
6568 match self {
66- TargetResolution :: Resolved ( candidate) => candidate. into_target ( required) ,
67- TargetResolution :: Ambiguous ( ambiguity) => {
68- let TargetAmbiguity { anchor, reason } = ambiguity;
69- let _ = ( anchor, reason) ;
70- None
69+ TargetResolution :: Resolved ( candidate) => {
70+ candidate. into_target ( required) . into_iter ( ) . collect ( )
7171 }
72+ TargetResolution :: Ambiguous ( alternatives) => alternatives. into_targets ( required) ,
7273 TargetResolution :: Blocked ( block) => {
7374 let TargetBlock { anchor, reason } = block;
7475 let _ = ( anchor, reason) ;
75- None
76+ Vec :: new ( )
7677 }
77- TargetResolution :: Unresolved => None ,
78+ TargetResolution :: Unresolved => Vec :: new ( ) ,
7879 }
7980 }
8081
@@ -87,6 +88,9 @@ impl<'tree> TargetResolution<'tree> {
8788 let capabilities = source_capabilities ( ) ;
8889 Self :: Resolved ( TargetCandidate :: new ( SemanticTarget :: Source ( target) , capabilities) )
8990 }
91+ SourceTargetResolution :: Ambiguous ( alternatives) => {
92+ Self :: from_source_alternatives ( file_id, alternatives)
93+ }
9094 SourceTargetResolution :: Blocked ( block) => {
9195 let SourceTargetBlock { range, .. } = block. clone ( ) ;
9296 let anchor = TargetAnchor {
@@ -99,6 +103,22 @@ impl<'tree> TargetResolution<'tree> {
99103 }
100104 }
101105
106+ fn from_source_alternatives (
107+ file_id : FileId ,
108+ alternatives : SourceTargetAlternatives < ' tree > ,
109+ ) -> Self {
110+ let SourceTargetAlternatives { domain, range, reason, targets } = alternatives;
111+ let anchor =
112+ TargetAnchor { file_id, range, origin : TargetOrigin :: from_source_domain ( domain) } ;
113+ let reason = TargetAmbiguityReason :: from_source ( reason) ;
114+ let capabilities = source_capabilities ( ) ;
115+ let candidates = targets
116+ . into_iter ( )
117+ . map ( |target| TargetCandidate :: new ( SemanticTarget :: Source ( target) , capabilities) )
118+ . collect ( ) ;
119+ Self :: Ambiguous ( TargetAlternatives { anchor, reason, candidates } )
120+ }
121+
102122 fn from_preproc_macro ( target : PreprocMacroTarget ) -> Self {
103123 let capabilities = target. capabilities ( ) ;
104124 Self :: Resolved ( TargetCandidate :: new ( SemanticTarget :: PreprocMacro ( target) , capabilities) )
@@ -118,9 +138,10 @@ impl<'tree> TargetResolution<'tree> {
118138 Self :: Blocked ( TargetBlock { anchor, reason : TargetBlockReason :: PreprocUnavailable } )
119139 }
120140 ( SourceTargetDomain :: Preproc , SourceTargetBlockReason :: Ambiguous { hits } ) => {
121- Self :: Ambiguous ( TargetAmbiguity {
141+ Self :: Ambiguous ( TargetAlternatives {
122142 anchor,
123- reason : TargetAmbiguityReason :: PreprocHits { candidate_count : hits. len ( ) } ,
143+ reason : TargetAmbiguityReason :: PreprocHits { hit_count : hits. len ( ) } ,
144+ candidates : Vec :: new ( ) ,
124145 } )
125146 }
126147 }
@@ -140,11 +161,15 @@ pub(crate) enum TargetOrigin {
140161}
141162
142163impl TargetOrigin {
143- fn from_source_block ( block : & SourceTargetBlock ) -> Self {
144- match block . domain {
164+ fn from_source_domain ( domain : SourceTargetDomain ) -> Self {
165+ match domain {
145166 SourceTargetDomain :: Preproc => TargetOrigin :: MacroExpansion ,
146167 }
147168 }
169+
170+ fn from_source_block ( block : & SourceTargetBlock ) -> Self {
171+ Self :: from_source_domain ( block. domain )
172+ }
148173}
149174
150175#[ derive( Debug , Clone ) ]
@@ -168,14 +193,33 @@ impl<'tree> TargetCandidate<'tree> {
168193}
169194
170195#[ derive( Debug , Clone ) ]
171- pub ( crate ) struct TargetAmbiguity {
196+ pub ( crate ) struct TargetAlternatives < ' tree > {
172197 pub anchor : TargetAnchor ,
173198 pub reason : TargetAmbiguityReason ,
199+ pub candidates : Vec < TargetCandidate < ' tree > > ,
200+ }
201+
202+ impl < ' tree > TargetAlternatives < ' tree > {
203+ fn into_targets ( self , required : TargetCapability ) -> Vec < SemanticTarget < ' tree > > {
204+ let Self { anchor, reason, candidates } = self ;
205+ let _ = ( anchor, reason) ;
206+ candidates. into_iter ( ) . filter_map ( |candidate| candidate. into_target ( required) ) . collect ( )
207+ }
174208}
175209
176210#[ derive( Debug , Clone , PartialEq , Eq ) ]
177211pub ( crate ) enum TargetAmbiguityReason {
178- PreprocHits { candidate_count : usize } ,
212+ PreprocHits { hit_count : usize } ,
213+ }
214+
215+ impl TargetAmbiguityReason {
216+ fn from_source ( reason : SourceTargetAmbiguity ) -> Self {
217+ match reason {
218+ SourceTargetAmbiguity :: PreprocHits { hit_count } => {
219+ TargetAmbiguityReason :: PreprocHits { hit_count }
220+ }
221+ }
222+ }
179223}
180224
181225#[ derive( Debug , Clone ) ]
@@ -345,11 +389,11 @@ mod tests {
345389 let resolution =
346390 resolve_semantic_target ( host. raw_db ( ) , file_id, offset, Some ( root) , token_precedence) ;
347391 assert ! ( matches!(
348- resolution. clone( ) . for_intent ( TargetIntent :: Describe ) ,
392+ resolution. clone( ) . unique_for_intent ( TargetIntent :: Describe ) ,
349393 Some ( SemanticTarget :: Source ( _) )
350394 ) ) ;
351395 assert ! ( matches!(
352- resolution. clone( ) . for_intent ( TargetIntent :: Rename ) ,
396+ resolution. clone( ) . unique_for_intent ( TargetIntent :: Rename ) ,
353397 Some ( SemanticTarget :: Source ( _) )
354398 ) ) ;
355399
@@ -403,6 +447,39 @@ mod tests {
403447 panic ! ( "conflicting source target should be ambiguous" ) ;
404448 } ;
405449
406- assert_eq ! ( ambiguity. reason, TargetAmbiguityReason :: PreprocHits { candidate_count: 0 } ) ;
450+ assert_eq ! ( ambiguity. reason, TargetAmbiguityReason :: PreprocHits { hit_count: 0 } ) ;
451+ assert ! ( ambiguity. candidates. is_empty( ) ) ;
452+ }
453+
454+ #[ test]
455+ fn ambiguous_source_target_alternatives_project_as_candidates ( ) {
456+ let range = TextRange :: new ( TextSize :: from ( 1 ) , TextSize :: from ( 4 ) ) ;
457+ let target_range = TextRange :: new ( TextSize :: from ( 2 ) , TextSize :: from ( 3 ) ) ;
458+ let target = crate :: source_targets:: SourceTarget {
459+ origin : crate :: source_targets:: SourceTargetOrigin :: NormalSyntax ,
460+ range : target_range,
461+ tokens : Vec :: new ( ) ,
462+ } ;
463+ let alternatives = crate :: source_targets:: SourceTargetAlternatives {
464+ domain : crate :: source_targets:: SourceTargetDomain :: Preproc ,
465+ range,
466+ reason : crate :: source_targets:: SourceTargetAmbiguity :: PreprocHits { hit_count : 2 } ,
467+ targets : vec ! [ target. clone( ) , target] ,
468+ } ;
469+
470+ let resolution = TargetResolution :: from_source_resolution (
471+ FileId ( 0 ) ,
472+ crate :: source_targets:: SourceTargetResolution :: Ambiguous ( alternatives) ,
473+ ) ;
474+
475+ assert ! ( resolution. clone( ) . unique_for_intent( TargetIntent :: Describe ) . is_none( ) ) ;
476+ assert_eq ! ( resolution. clone( ) . targets_for_intent( TargetIntent :: Describe ) . len( ) , 2 ) ;
477+
478+ let TargetResolution :: Ambiguous ( alternatives) = resolution else {
479+ panic ! ( "source alternatives should stay ambiguous" ) ;
480+ } ;
481+ assert_eq ! ( alternatives. anchor. range, range) ;
482+ assert_eq ! ( alternatives. reason, TargetAmbiguityReason :: PreprocHits { hit_count: 2 } ) ;
483+ assert_eq ! ( alternatives. candidates. len( ) , 2 ) ;
407484 }
408485}
0 commit comments