@@ -75,6 +75,18 @@ pub(super) enum ItemListKind {
7575 ExternBlock ,
7676}
7777
78+ #[ derive( Debug , Default ) ]
79+ pub ( super ) struct QualifierCtx {
80+ pub ( super ) unsafe_tok : Option < SyntaxToken > ,
81+ pub ( super ) vis_node : Option < ast:: Visibility > ,
82+ }
83+
84+ impl QualifierCtx {
85+ pub ( super ) fn none ( & self ) -> bool {
86+ self . unsafe_tok . is_none ( ) && self . vis_node . is_none ( )
87+ }
88+ }
89+
7890#[ derive( Debug ) ]
7991pub ( crate ) struct PathCompletionCtx {
8092 /// If this is a call with () already there (or {} in case of record patterns)
@@ -253,6 +265,7 @@ pub(crate) struct CompletionContext<'a> {
253265 pub ( super ) ident_ctx : IdentContext ,
254266
255267 pub ( super ) pattern_ctx : Option < PatternContext > ,
268+ pub ( super ) qualifier_ctx : QualifierCtx ,
256269
257270 pub ( super ) existing_derives : FxHashSet < hir:: Macro > ,
258271
@@ -363,17 +376,13 @@ impl<'a> CompletionContext<'a> {
363376 matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: ImplDefType ) )
364377 }
365378
366- pub ( crate ) fn has_visibility_prev_sibling ( & self ) -> bool {
367- matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: Visibility ) )
368- }
369-
370379 pub ( crate ) fn after_if ( & self ) -> bool {
371380 matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: IfExpr ) )
372381 }
373382
374383 // FIXME: This shouldn't exist
375384 pub ( crate ) fn is_path_disallowed ( & self ) -> bool {
376- self . previous_token_is ( T ! [ unsafe ] )
385+ ! self . qualifier_ctx . none ( )
377386 || matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: Visibility ) )
378387 || ( matches ! ( self . name_ctx( ) , Some ( NameContext { .. } ) ) && self . pattern_ctx . is_none ( ) )
379388 || matches ! ( self . pattern_ctx, Some ( PatternContext { record_pat: Some ( _) , .. } ) )
@@ -555,6 +564,7 @@ impl<'a> CompletionContext<'a> {
555564 // dummy value, will be overwritten
556565 ident_ctx : IdentContext :: UnexpandedAttrTT { fake_attribute_under_caret : None } ,
557566 pattern_ctx : None ,
567+ qualifier_ctx : Default :: default ( ) ,
558568 existing_derives : Default :: default ( ) ,
559569 locals,
560570 } ;
@@ -865,7 +875,7 @@ impl<'a> CompletionContext<'a> {
865875 offset : TextSize ,
866876 derive_ctx : Option < ( SyntaxNode , SyntaxNode , TextSize , ast:: Attr ) > ,
867877 ) -> Option < ( ) > {
868- let fake_ident_token = file_with_fake_ident. token_at_offset ( offset) . right_biased ( ) . unwrap ( ) ;
878+ let fake_ident_token = file_with_fake_ident. token_at_offset ( offset) . right_biased ( ) ? ;
869879 let syntax_element = NodeOrToken :: Token ( fake_ident_token) ;
870880 if is_in_token_of_for_loop ( syntax_element. clone ( ) ) {
871881 // for pat $0
@@ -967,7 +977,49 @@ impl<'a> CompletionContext<'a> {
967977 ast:: NameLike :: NameRef ( name_ref) => {
968978 let parent = name_ref. syntax ( ) . parent ( ) ?;
969979 let ( nameref_ctx, pat_ctx) =
970- Self :: classify_name_ref ( & self . sema , & original_file, name_ref, parent) ;
980+ Self :: classify_name_ref ( & self . sema , & original_file, name_ref, parent. clone ( ) ) ;
981+
982+ // Extract qualifiers
983+ if let Some ( path_ctx) = & nameref_ctx. path_ctx {
984+ if path_ctx. qualifier . is_none ( ) {
985+ let top = match path_ctx. kind {
986+ PathKind :: Expr { in_block_expr : true , .. } => parent
987+ . ancestors ( )
988+ . find ( |it| ast:: PathExpr :: can_cast ( it. kind ( ) ) )
989+ . and_then ( |p| {
990+ let parent = p. parent ( ) ?;
991+ if ast:: StmtList :: can_cast ( parent. kind ( ) ) {
992+ Some ( p)
993+ } else if ast:: ExprStmt :: can_cast ( parent. kind ( ) ) {
994+ Some ( parent)
995+ } else {
996+ None
997+ }
998+ } ) ,
999+ PathKind :: Item { .. } => {
1000+ parent. ancestors ( ) . find ( |it| ast:: MacroCall :: can_cast ( it. kind ( ) ) )
1001+ }
1002+ _ => None ,
1003+ } ;
1004+ if let Some ( top) = top {
1005+ if let Some ( NodeOrToken :: Node ( error_node) ) =
1006+ syntax:: algo:: non_trivia_sibling (
1007+ top. into ( ) ,
1008+ syntax:: Direction :: Prev ,
1009+ )
1010+ {
1011+ if error_node. kind ( ) == SyntaxKind :: ERROR {
1012+ self . qualifier_ctx . unsafe_tok = error_node
1013+ . children_with_tokens ( )
1014+ . filter_map ( NodeOrToken :: into_token)
1015+ . find ( |it| it. kind ( ) == T ! [ unsafe ] ) ;
1016+ self . qualifier_ctx . vis_node =
1017+ error_node. children ( ) . find_map ( ast:: Visibility :: cast) ;
1018+ }
1019+ }
1020+ }
1021+ }
1022+ }
9711023 self . ident_ctx = IdentContext :: NameRef ( nameref_ctx) ;
9721024 self . pattern_ctx = pat_ctx;
9731025 }
@@ -1145,12 +1197,54 @@ impl<'a> CompletionContext<'a> {
11451197 }
11461198 } ;
11471199
1200+ // We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
1201+ // ex. trait Foo $0 {}
1202+ // in these cases parser recovery usually kicks in for our inserted identifier, causing it
1203+ // to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block
1204+ // expression or an item list.
1205+ // The following code checks if the body is missing, if it is we either cut off the body
1206+ // from the item or it was missing in the first place
1207+ let inbetween_body_and_decl_check = |node : SyntaxNode | {
1208+ if let Some ( NodeOrToken :: Node ( n) ) =
1209+ syntax:: algo:: non_trivia_sibling ( node. into ( ) , syntax:: Direction :: Prev )
1210+ {
1211+ if let Some ( item) = ast:: Item :: cast ( n) {
1212+ match item {
1213+ ast:: Item :: Const ( it) => it. body ( ) . is_none ( ) ,
1214+ ast:: Item :: Enum ( it) => it. variant_list ( ) . is_none ( ) ,
1215+ ast:: Item :: ExternBlock ( it) => it. extern_item_list ( ) . is_none ( ) ,
1216+ ast:: Item :: Fn ( it) => it. body ( ) . is_none ( ) ,
1217+ ast:: Item :: Impl ( it) => it. assoc_item_list ( ) . is_none ( ) ,
1218+ ast:: Item :: Module ( it) => it. item_list ( ) . is_none ( ) ,
1219+ ast:: Item :: Static ( it) => it. body ( ) . is_none ( ) ,
1220+ ast:: Item :: Struct ( it) => it. field_list ( ) . is_none ( ) ,
1221+ ast:: Item :: Trait ( it) => it. assoc_item_list ( ) . is_none ( ) ,
1222+ ast:: Item :: TypeAlias ( it) => it. ty ( ) . is_none ( ) ,
1223+ ast:: Item :: Union ( it) => it. record_field_list ( ) . is_none ( ) ,
1224+ _ => false ,
1225+ }
1226+ } else {
1227+ false
1228+ }
1229+ } else {
1230+ false
1231+ }
1232+ } ;
1233+
11481234 let kind = path. syntax ( ) . ancestors ( ) . find_map ( |it| {
11491235 // using Option<Option<PathKind>> as extra controlflow
11501236 let kind = match_ast ! {
11511237 match it {
11521238 ast:: PathType ( _) => Some ( PathKind :: Type ) ,
11531239 ast:: PathExpr ( it) => {
1240+ if let Some ( p) = it. syntax( ) . parent( ) {
1241+ if ast:: ExprStmt :: can_cast( p. kind( ) ) {
1242+ if inbetween_body_and_decl_check( p) {
1243+ return Some ( None ) ;
1244+ }
1245+ }
1246+ }
1247+
11541248 fill_record_expr( it. syntax( ) ) ;
11551249
11561250 path_ctx. has_call_parens = it. syntax( ) . parent( ) . map_or( false , |it| ast:: CallExpr :: can_cast( it. kind( ) ) ) ;
@@ -1173,6 +1267,10 @@ impl<'a> CompletionContext<'a> {
11731267 Some ( PathKind :: Pat )
11741268 } ,
11751269 ast:: MacroCall ( it) => {
1270+ if inbetween_body_and_decl_check( it. syntax( ) . clone( ) ) {
1271+ return Some ( None ) ;
1272+ }
1273+
11761274 path_ctx. has_macro_bang = it. excl_token( ) . is_some( ) ;
11771275 let parent = it. syntax( ) . parent( ) ;
11781276 match parent. as_ref( ) . map( |it| it. kind( ) ) {
0 commit comments