@@ -24,7 +24,7 @@ use syntax::{
2424 SyntaxNode , TextRange , TextSize , T ,
2525} ;
2626
27- use crate :: { FilePosition , NavigationTarget , TryToNav } ;
27+ use crate :: { highlight_related , FilePosition , HighlightedRange , NavigationTarget , TryToNav } ;
2828
2929#[ derive( Debug , Clone ) ]
3030pub struct ReferenceSearchResult {
@@ -103,6 +103,11 @@ pub(crate) fn find_all_refs(
103103 }
104104 } ;
105105
106+ // Find references for control-flow keywords.
107+ if let Some ( res) = handle_control_flow_keywords ( sema, position) {
108+ return Some ( vec ! [ res] ) ;
109+ }
110+
106111 match name_for_constructor_search ( & syntax, position) {
107112 Some ( name) => {
108113 let def = match NameClass :: classify ( sema, & name) ? {
@@ -296,6 +301,34 @@ fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool {
296301 } ) . unwrap_or ( false )
297302}
298303
304+ fn handle_control_flow_keywords (
305+ sema : & Semantics < ' _ , RootDatabase > ,
306+ FilePosition { file_id, offset } : FilePosition ,
307+ ) -> Option < ReferenceSearchResult > {
308+ let file = sema. parse ( file_id) ;
309+ let token = file. syntax ( ) . token_at_offset ( offset) . find ( |t| t. kind ( ) . is_keyword ( ) ) ?;
310+
311+ let refs = match token. kind ( ) {
312+ T ! [ fn ] | T ! [ return ] | T ! [ try] => highlight_related:: highlight_exit_points ( sema, token) ?,
313+ T ! [ async ] => highlight_related:: highlight_yield_points ( sema, token) ?,
314+ T ! [ loop ] | T ! [ while ] | T ! [ break ] | T ! [ continue ] => {
315+ highlight_related:: highlight_break_points ( sema, token) ?
316+ }
317+ T ! [ for ] if token. parent ( ) . and_then ( ast:: ForExpr :: cast) . is_some ( ) => {
318+ highlight_related:: highlight_break_points ( sema, token) ?
319+ }
320+ _ => return None ,
321+ }
322+ . into_iter ( )
323+ . map ( |HighlightedRange { range, category } | ( range, category) )
324+ . collect ( ) ;
325+
326+ Some ( ReferenceSearchResult {
327+ declaration : None ,
328+ references : IntMap :: from_iter ( [ ( file_id, refs) ] ) ,
329+ } )
330+ }
331+
299332#[ cfg( test) ]
300333mod tests {
301334 use expect_test:: { expect, Expect } ;
@@ -2187,4 +2220,223 @@ fn test() {
21872220 "# ] ] ,
21882221 ) ;
21892222 }
2223+
2224+ #[ test]
2225+ fn goto_ref_fn_kw ( ) {
2226+ check (
2227+ r#"
2228+ macro_rules! N {
2229+ ($i:ident, $x:expr, $blk:expr) => {
2230+ for $i in 0..$x {
2231+ $blk
2232+ }
2233+ };
2234+ }
2235+
2236+ fn main() {
2237+ $0fn f() {
2238+ N!(i, 5, {
2239+ println!("{}", i);
2240+ return;
2241+ });
2242+
2243+ for i in 1..5 {
2244+ return;
2245+ }
2246+
2247+ (|| {
2248+ return;
2249+ })();
2250+ }
2251+ }
2252+ "# ,
2253+ expect ! [ [ r#"
2254+ FileId(0) 136..138
2255+ FileId(0) 207..213
2256+ FileId(0) 264..270
2257+ "# ] ] ,
2258+ )
2259+ }
2260+
2261+ #[ test]
2262+ fn goto_ref_exit_points ( ) {
2263+ check (
2264+ r#"
2265+ fn$0 foo() -> u32 {
2266+ if true {
2267+ return 0;
2268+ }
2269+
2270+ 0?;
2271+ 0xDEAD_BEEF
2272+ }
2273+ "# ,
2274+ expect ! [ [ r#"
2275+ FileId(0) 0..2
2276+ FileId(0) 62..63
2277+ FileId(0) 40..46
2278+ FileId(0) 69..80
2279+ "# ] ] ,
2280+ ) ;
2281+ }
2282+
2283+ #[ test]
2284+ fn test_ref_yield_points ( ) {
2285+ check (
2286+ r#"
2287+ pub async$0 fn foo() {
2288+ let x = foo()
2289+ .await
2290+ .await;
2291+ || { 0.await };
2292+ (async { 0.await }).await
2293+ }
2294+ "# ,
2295+ expect ! [ [ r#"
2296+ FileId(0) 4..9
2297+ FileId(0) 63..68
2298+ FileId(0) 48..53
2299+ FileId(0) 114..119
2300+ "# ] ] ,
2301+ ) ;
2302+ }
2303+
2304+ #[ test]
2305+ fn goto_ref_for_kw ( ) {
2306+ check (
2307+ r#"
2308+ fn main() {
2309+ $0for i in 1..5 {
2310+ break;
2311+ continue;
2312+ }
2313+ }
2314+ "# ,
2315+ expect ! [ [ r#"
2316+ FileId(0) 16..19
2317+ FileId(0) 40..45
2318+ FileId(0) 55..63
2319+ "# ] ] ,
2320+ )
2321+ }
2322+
2323+ #[ test]
2324+ fn goto_ref_on_break_kw ( ) {
2325+ check (
2326+ r#"
2327+ fn main() {
2328+ for i in 1..5 {
2329+ $0break;
2330+ continue;
2331+ }
2332+ }
2333+ "# ,
2334+ expect ! [ [ r#"
2335+ FileId(0) 16..19
2336+ FileId(0) 40..45
2337+ "# ] ] ,
2338+ )
2339+ }
2340+
2341+ #[ test]
2342+ fn goto_ref_on_break_kw_for_block ( ) {
2343+ check (
2344+ r#"
2345+ fn main() {
2346+ 'a:{
2347+ $0break 'a;
2348+ }
2349+ }
2350+ "# ,
2351+ expect ! [ [ r#"
2352+ FileId(0) 16..19
2353+ FileId(0) 29..37
2354+ "# ] ] ,
2355+ )
2356+ }
2357+
2358+ #[ test]
2359+ fn goto_ref_on_break_with_label ( ) {
2360+ check (
2361+ r#"
2362+ fn foo() {
2363+ 'outer: loop {
2364+ break;
2365+ 'inner: loop {
2366+ 'innermost: loop {
2367+ }
2368+ $0break 'outer;
2369+ break;
2370+ }
2371+ break;
2372+ }
2373+ }
2374+ "# ,
2375+ expect ! [ [ r#"
2376+ FileId(0) 15..27
2377+ FileId(0) 39..44
2378+ FileId(0) 127..139
2379+ FileId(0) 178..183
2380+ "# ] ] ,
2381+ ) ;
2382+ }
2383+
2384+ #[ test]
2385+ fn goto_ref_on_return_in_try ( ) {
2386+ check (
2387+ r#"
2388+ fn main() {
2389+ fn f() {
2390+ try {
2391+ $0return;
2392+ }
2393+
2394+ return;
2395+ }
2396+ return;
2397+ }
2398+ "# ,
2399+ expect ! [ [ r#"
2400+ FileId(0) 16..18
2401+ FileId(0) 51..57
2402+ FileId(0) 78..84
2403+ "# ] ] ,
2404+ )
2405+ }
2406+
2407+ #[ test]
2408+ fn goto_ref_on_break_in_try ( ) {
2409+ check (
2410+ r#"
2411+ fn main() {
2412+ for i in 1..100 {
2413+ let x: Result<(), ()> = try {
2414+ $0break;
2415+ };
2416+ }
2417+ }
2418+ "# ,
2419+ expect ! [ [ r#"
2420+ FileId(0) 16..19
2421+ FileId(0) 84..89
2422+ "# ] ] ,
2423+ )
2424+ }
2425+
2426+ #[ test]
2427+ fn goto_ref_on_return_in_async_block ( ) {
2428+ check (
2429+ r#"
2430+ fn main() {
2431+ $0async {
2432+ return;
2433+ }
2434+ }
2435+ "# ,
2436+ expect ! [ [ r#"
2437+ FileId(0) 16..21
2438+ FileId(0) 32..38
2439+ "# ] ] ,
2440+ )
2441+ }
21902442}
0 commit comments