1+ use std:: { collections:: HashSet , ops:: ControlFlow } ;
2+
13use either:: Either ;
24use hir:: { AsAssocItem , HasAttrs , HasSource , HirDisplay , Semantics , TypeInfo } ;
35use ide_db:: {
@@ -13,7 +15,7 @@ use itertools::Itertools;
1315use stdx:: format_to;
1416use syntax:: {
1517 algo, ast, display:: fn_as_proc_macro_label, match_ast, AstNode , Direction , SyntaxKind :: * ,
16- SyntaxNode , SyntaxToken , T ,
18+ SyntaxNode , SyntaxToken , TextRange , TextSize , T ,
1719} ;
1820
1921use crate :: {
@@ -111,16 +113,69 @@ pub(crate) fn hover(
111113 kind if kind. is_trivia ( ) => 0 ,
112114 _ => 1 ,
113115 } ) ?;
114- let token = sema. descend_into_macros ( token) ;
115- let node = token. parent ( ) ?;
116+
117+ let mut seen = HashSet :: default ( ) ;
118+
119+ let mut fallback = None ;
120+ sema. descend_into_macros_many ( token. clone ( ) )
121+ . iter ( )
122+ . filter_map ( |token| match token. parent ( ) {
123+ Some ( node) => {
124+ match find_hover_result ( & sema, file_id, offset, config, token, & node, & mut seen) {
125+ Some ( res) => match res {
126+ ControlFlow :: Break ( inner) => Some ( inner) ,
127+ ControlFlow :: Continue ( _) => {
128+ if fallback. is_none ( ) {
129+ // FIXME we're only taking the first fallback into account that's not `None`
130+ fallback = hover_for_keyword ( & sema, config, & token)
131+ . or ( type_hover ( & sema, config, & token) ) ;
132+ }
133+ None
134+ }
135+ } ,
136+ None => None ,
137+ }
138+ }
139+ None => None ,
140+ } )
141+ // reduce all descends into a single `RangeInfo`
142+ // that spans from the earliest start to the latest end (fishy/FIXME),
143+ // concatenates all `Markup`s with `\n---\n`,
144+ // and accumulates all actions into its `actions` vector.
145+ . reduce ( |mut acc, RangeInfo { range, mut info } | {
146+ let start = acc. range . start ( ) . min ( range. start ( ) ) ;
147+ let end = acc. range . end ( ) . max ( range. end ( ) ) ;
148+
149+ acc. range = TextRange :: new ( start, end) ;
150+ acc. info . actions . append ( & mut info. actions ) ;
151+ acc. info . markup = Markup :: from ( format ! ( "{}\n ---\n {}" , acc. info. markup, info. markup) ) ;
152+ acc
153+ } )
154+ . or ( fallback)
155+ }
156+
157+ fn find_hover_result (
158+ sema : & Semantics < RootDatabase > ,
159+ file_id : FileId ,
160+ offset : TextSize ,
161+ config : & HoverConfig ,
162+ token : & SyntaxToken ,
163+ node : & SyntaxNode ,
164+ seen : & mut HashSet < Definition > ,
165+ ) -> Option < ControlFlow < RangeInfo < HoverResult > > > {
116166 let mut range_override = None ;
167+
168+ // intra-doc links and attributes are special cased
169+ // so don't add them to the `seen` duplicate check
170+ let mut add_to_seen_definitions = true ;
171+
117172 let definition = match_ast ! {
118173 match node {
119- ast:: Name ( name) => NameClass :: classify( & sema, & name) . map( |class| match class {
174+ ast:: Name ( name) => NameClass :: classify( sema, & name) . map( |class| match class {
120175 NameClass :: Definition ( it) | NameClass :: ConstReference ( it) => it,
121176 NameClass :: PatFieldShorthand { local_def, field_ref: _ } => Definition :: Local ( local_def) ,
122177 } ) ,
123- ast:: NameRef ( name_ref) => NameRefClass :: classify( & sema, & name_ref) . map( |class| match class {
178+ ast:: NameRef ( name_ref) => NameRefClass :: classify( sema, & name_ref) . map( |class| match class {
124179 NameRefClass :: Definition ( def) => def,
125180 NameRefClass :: FieldShorthand { local_ref: _, field_ref } => {
126181 Definition :: Field ( field_ref)
@@ -137,25 +192,37 @@ pub(crate) fn hover(
137192 ) ,
138193 _ => {
139194 // intra-doc links
195+ // FIXME: move comment + attribute special cases somewhere else to simplify control flow,
196+ // hopefully simplifying the return type of this function in the process
197+ // (the `Break`/`Continue` distinction is needed to decide whether to use fallback hovers)
198+ //
199+ // FIXME: hovering the intra doc link to `Foo` not working:
200+ //
201+ // #[identity]
202+ // trait Foo {
203+ // /// [`Foo`]
204+ // fn foo() {}
140205 if token. kind( ) == COMMENT {
206+ add_to_seen_definitions = false ;
141207 cov_mark:: hit!( no_highlight_on_comment_hover) ;
142- let ( attributes, def) = doc_attributes( & sema, & node) ?;
143- let ( docs, doc_mapping) = attributes. docs_with_rangemap( db) ?;
208+ let ( attributes, def) = doc_attributes( sema, node) ?;
209+ let ( docs, doc_mapping) = attributes. docs_with_rangemap( sema . db) ?;
144210 let ( idl_range, link, ns) =
145211 extract_definitions_from_docs( & docs) . into_iter( ) . find_map( |( range, link, ns) | {
146212 let mapped = doc_mapping. map( range) ?;
147213 ( mapped. file_id == file_id. into( ) && mapped. value. contains( offset) ) . then( ||( mapped. value, link, ns) )
148214 } ) ?;
149215 range_override = Some ( idl_range) ;
150- Some ( match resolve_doc_path_for_def( db, def, & link, ns) ? {
216+ Some ( match resolve_doc_path_for_def( sema . db, def, & link, ns) ? {
151217 Either :: Left ( it) => Definition :: ModuleDef ( it) ,
152218 Either :: Right ( it) => Definition :: Macro ( it) ,
153219 } )
154220 // attributes, require special machinery as they are mere ident tokens
155221 } else if let Some ( attr) = token. ancestors( ) . find_map( ast:: Attr :: cast) {
222+ add_to_seen_definitions = false ;
156223 // lints
157- if let res@ Some ( _ ) = try_hover_for_lint( & attr, & token) {
158- return res;
224+ if let Some ( res ) = try_hover_for_lint( & attr, & token) {
225+ return Some ( ControlFlow :: Break ( res) ) ;
159226 // derives
160227 } else {
161228 range_override = Some ( token. text_range( ) ) ;
@@ -169,42 +236,53 @@ pub(crate) fn hover(
169236 } ;
170237
171238 if let Some ( definition) = definition {
239+ // skip duplicates
240+ if seen. contains ( & definition) {
241+ return None ;
242+ }
243+ if add_to_seen_definitions {
244+ seen. insert ( definition) ;
245+ }
172246 let famous_defs = match & definition {
173247 Definition :: ModuleDef ( hir:: ModuleDef :: BuiltinType ( _) ) => {
174248 Some ( FamousDefs ( & sema, sema. scope ( & node) . krate ( ) ) )
175249 }
176250 _ => None ,
177251 } ;
178- if let Some ( markup) = hover_for_definition ( db, definition, famous_defs. as_ref ( ) , config) {
252+ if let Some ( markup) =
253+ hover_for_definition ( sema. db , definition, famous_defs. as_ref ( ) , config)
254+ {
179255 let mut res = HoverResult :: default ( ) ;
180256 res. markup = process_markup ( sema. db , definition, & markup, config) ;
181- if let Some ( action) = show_implementations_action ( db, definition) {
257+ if let Some ( action) = show_implementations_action ( sema . db , definition) {
182258 res. actions . push ( action) ;
183259 }
184260
185- if let Some ( action) = show_fn_references_action ( db, definition) {
261+ if let Some ( action) = show_fn_references_action ( sema . db , definition) {
186262 res. actions . push ( action) ;
187263 }
188264
189265 if let Some ( action) = runnable_action ( & sema, definition, file_id) {
190266 res. actions . push ( action) ;
191267 }
192268
193- if let Some ( action) = goto_type_action_for_def ( db, definition) {
269+ if let Some ( action) = goto_type_action_for_def ( sema . db , definition) {
194270 res. actions . push ( action) ;
195271 }
196272
197273 let range = range_override. unwrap_or_else ( || sema. original_range ( & node) . range ) ;
198- return Some ( RangeInfo :: new ( range, res) ) ;
274+ return Some ( ControlFlow :: Break ( RangeInfo :: new ( range, res) ) ) ;
199275 }
200276 }
201277
202- if let res @ Some ( _) = hover_for_keyword ( & sema, config, & token) {
203- return res;
204- }
205-
206- // No definition below cursor, fall back to showing type hovers.
278+ Some ( ControlFlow :: Continue ( ( ) ) )
279+ }
207280
281+ fn type_hover (
282+ sema : & Semantics < RootDatabase > ,
283+ config : & HoverConfig ,
284+ token : & SyntaxToken ,
285+ ) -> Option < RangeInfo < HoverResult > > {
208286 let node = token
209287 . ancestors ( )
210288 . take_while ( |it| !ast:: Item :: can_cast ( it. kind ( ) ) )
@@ -214,7 +292,7 @@ pub(crate) fn hover(
214292 match node {
215293 ast:: Expr ( it) => Either :: Left ( it) ,
216294 ast:: Pat ( it) => Either :: Right ( it) ,
217- // If this node is a MACRO_CALL, it means that `descend_into_macros ` failed to resolve.
295+ // If this node is a MACRO_CALL, it means that `descend_into_macros_many ` failed to resolve.
218296 // (e.g expanding a builtin macro). So we give up here.
219297 ast:: MacroCall ( _it) => return None ,
220298 _ => return None ,
@@ -914,6 +992,82 @@ mod tests {
914992 assert ! ( hover. is_none( ) ) ;
915993 }
916994
995+ #[ test]
996+ fn hover_descend_macros_avoids_duplicates ( ) {
997+ check (
998+ r#"
999+ macro_rules! dupe_use {
1000+ ($local:ident) => {
1001+ {
1002+ $local;
1003+ $local;
1004+ }
1005+ }
1006+ }
1007+ fn foo() {
1008+ let local = 0;
1009+ dupe_use!(local$0);
1010+ }
1011+ "# ,
1012+ expect ! [ [ r#"
1013+ *local*
1014+
1015+ ```rust
1016+ let local: i32
1017+ ```
1018+ "# ] ] ,
1019+ ) ;
1020+ }
1021+
1022+ #[ test]
1023+ fn hover_shows_all_macro_descends ( ) {
1024+ check (
1025+ r#"
1026+ macro_rules! m {
1027+ ($name:ident) => {
1028+ /// Outer
1029+ fn $name() {}
1030+
1031+ mod module {
1032+ /// Inner
1033+ fn $name() {}
1034+ }
1035+ };
1036+ }
1037+
1038+ m!(ab$0c);
1039+ "# ,
1040+ expect ! [ [ r#"
1041+ *abc*
1042+
1043+ ```rust
1044+ test::module
1045+ ```
1046+
1047+ ```rust
1048+ fn abc()
1049+ ```
1050+
1051+ ---
1052+
1053+ Inner
1054+ ---
1055+
1056+ ```rust
1057+ test
1058+ ```
1059+
1060+ ```rust
1061+ fn abc()
1062+ ```
1063+
1064+ ---
1065+
1066+ Outer
1067+ "# ] ] ,
1068+ ) ;
1069+ }
1070+
9171071 #[ test]
9181072 fn hover_shows_type_of_an_expression ( ) {
9191073 check (
0 commit comments