@@ -75,6 +75,18 @@ pub(super) enum ItemListKind {
75
75
ExternBlock ,
76
76
}
77
77
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
+
78
90
#[ derive( Debug ) ]
79
91
pub ( crate ) struct PathCompletionCtx {
80
92
/// If this is a call with () already there (or {} in case of record patterns)
@@ -253,6 +265,7 @@ pub(crate) struct CompletionContext<'a> {
253
265
pub ( super ) ident_ctx : IdentContext ,
254
266
255
267
pub ( super ) pattern_ctx : Option < PatternContext > ,
268
+ pub ( super ) qualifier_ctx : QualifierCtx ,
256
269
257
270
pub ( super ) existing_derives : FxHashSet < hir:: Macro > ,
258
271
@@ -363,17 +376,13 @@ impl<'a> CompletionContext<'a> {
363
376
matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: ImplDefType ) )
364
377
}
365
378
366
- pub ( crate ) fn has_visibility_prev_sibling ( & self ) -> bool {
367
- matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: Visibility ) )
368
- }
369
-
370
379
pub ( crate ) fn after_if ( & self ) -> bool {
371
380
matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: IfExpr ) )
372
381
}
373
382
374
383
// FIXME: This shouldn't exist
375
384
pub ( crate ) fn is_path_disallowed ( & self ) -> bool {
376
- self . previous_token_is ( T ! [ unsafe ] )
385
+ ! self . qualifier_ctx . none ( )
377
386
|| matches ! ( self . prev_sibling, Some ( ImmediatePrevSibling :: Visibility ) )
378
387
|| ( matches ! ( self . name_ctx( ) , Some ( NameContext { .. } ) ) && self . pattern_ctx . is_none ( ) )
379
388
|| matches ! ( self . pattern_ctx, Some ( PatternContext { record_pat: Some ( _) , .. } ) )
@@ -555,6 +564,7 @@ impl<'a> CompletionContext<'a> {
555
564
// dummy value, will be overwritten
556
565
ident_ctx : IdentContext :: UnexpandedAttrTT { fake_attribute_under_caret : None } ,
557
566
pattern_ctx : None ,
567
+ qualifier_ctx : Default :: default ( ) ,
558
568
existing_derives : Default :: default ( ) ,
559
569
locals,
560
570
} ;
@@ -865,7 +875,7 @@ impl<'a> CompletionContext<'a> {
865
875
offset : TextSize ,
866
876
derive_ctx : Option < ( SyntaxNode , SyntaxNode , TextSize , ast:: Attr ) > ,
867
877
) -> 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 ( ) ? ;
869
879
let syntax_element = NodeOrToken :: Token ( fake_ident_token) ;
870
880
if is_in_token_of_for_loop ( syntax_element. clone ( ) ) {
871
881
// for pat $0
@@ -967,7 +977,49 @@ impl<'a> CompletionContext<'a> {
967
977
ast:: NameLike :: NameRef ( name_ref) => {
968
978
let parent = name_ref. syntax ( ) . parent ( ) ?;
969
979
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
+ }
971
1023
self . ident_ctx = IdentContext :: NameRef ( nameref_ctx) ;
972
1024
self . pattern_ctx = pat_ctx;
973
1025
}
@@ -1145,12 +1197,54 @@ impl<'a> CompletionContext<'a> {
1145
1197
}
1146
1198
} ;
1147
1199
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
+
1148
1234
let kind = path. syntax ( ) . ancestors ( ) . find_map ( |it| {
1149
1235
// using Option<Option<PathKind>> as extra controlflow
1150
1236
let kind = match_ast ! {
1151
1237
match it {
1152
1238
ast:: PathType ( _) => Some ( PathKind :: Type ) ,
1153
1239
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
+
1154
1248
fill_record_expr( it. syntax( ) ) ;
1155
1249
1156
1250
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> {
1173
1267
Some ( PathKind :: Pat )
1174
1268
} ,
1175
1269
ast:: MacroCall ( it) => {
1270
+ if inbetween_body_and_decl_check( it. syntax( ) . clone( ) ) {
1271
+ return Some ( None ) ;
1272
+ }
1273
+
1176
1274
path_ctx. has_macro_bang = it. excl_token( ) . is_some( ) ;
1177
1275
let parent = it. syntax( ) . parent( ) ;
1178
1276
match parent. as_ref( ) . map( |it| it. kind( ) ) {
0 commit comments