@@ -276,6 +276,135 @@ fn partial_is_function_keyword_after_modifier() {
276276 assert ! ( ctx. is_none( ) ) ;
277277}
278278
279+ // ── Partial keyword prefixes after modifiers ────────────────────
280+
281+ #[ test]
282+ fn partial_fu_after_public_is_not_type_hint ( ) {
283+ // `public fu` — "fu" is a prefix of "function", should defer to keyword completion.
284+ let src = "<?php\n class Foo {\n public fu\n }" ;
285+ let ctx = detect ( src, 2 , 13 ) ;
286+ assert ! (
287+ ctx. is_none( ) ,
288+ "partial 'fu' is a prefix of 'function' and should not trigger type hints"
289+ ) ;
290+ }
291+
292+ #[ test]
293+ fn partial_fun_after_public_is_not_type_hint ( ) {
294+ let src = "<?php\n class Foo {\n public fun\n }" ;
295+ let ctx = detect ( src, 2 , 14 ) ;
296+ assert ! (
297+ ctx. is_none( ) ,
298+ "partial 'fun' is a prefix of 'function' and should not trigger type hints"
299+ ) ;
300+ }
301+
302+ #[ test]
303+ fn partial_func_after_public_is_not_type_hint ( ) {
304+ let src = "<?php\n class Foo {\n public func\n }" ;
305+ let ctx = detect ( src, 2 , 15 ) ;
306+ assert ! (
307+ ctx. is_none( ) ,
308+ "partial 'func' is a prefix of 'function' and should not trigger type hints"
309+ ) ;
310+ }
311+
312+ #[ test]
313+ fn partial_con_after_public_is_not_type_hint ( ) {
314+ // `public con` — "con" is a prefix of "const", should defer to keyword completion.
315+ let src = "<?php\n class Foo {\n public con\n }" ;
316+ let ctx = detect ( src, 2 , 14 ) ;
317+ assert ! (
318+ ctx. is_none( ) ,
319+ "partial 'con' is a prefix of 'const' and should not trigger type hints"
320+ ) ;
321+ }
322+
323+ #[ test]
324+ fn partial_st_after_public_is_not_type_hint ( ) {
325+ // `public st` — "st" is a prefix of "static", should defer to keyword completion.
326+ let src = "<?php\n class Foo {\n public st\n }" ;
327+ let ctx = detect ( src, 2 , 13 ) ;
328+ assert ! (
329+ ctx. is_none( ) ,
330+ "partial 'st' is a prefix of 'static' and should not trigger type hints"
331+ ) ;
332+ }
333+
334+ #[ test]
335+ fn partial_f_after_public_is_not_type_hint ( ) {
336+ // `public f` — "f" is a prefix of "function", "fn", "final".
337+ let src = "<?php\n class Foo {\n public f\n }" ;
338+ let ctx = detect ( src, 2 , 12 ) ;
339+ assert ! (
340+ ctx. is_none( ) ,
341+ "partial 'f' is a prefix of 'function'/'fn'/'final' and should not trigger type hints"
342+ ) ;
343+ }
344+
345+ #[ test]
346+ fn partial_cl_after_readonly_at_top_level_is_not_type_hint ( ) {
347+ // `readonly cl` — "cl" is a prefix of "class", should defer to keyword completion.
348+ let src = "<?php\n readonly cl" ;
349+ let ctx = detect ( src, 1 , 12 ) ;
350+ assert ! (
351+ ctx. is_none( ) ,
352+ "partial 'cl' is a prefix of 'class' and should not trigger type hints"
353+ ) ;
354+ }
355+
356+ #[ test]
357+ fn partial_ab_after_public_is_not_type_hint ( ) {
358+ // `public ab` — "ab" is a prefix of "abstract".
359+ let src = "<?php\n class Foo {\n public ab\n }" ;
360+ let ctx = detect ( src, 2 , 13 ) ;
361+ assert ! (
362+ ctx. is_none( ) ,
363+ "partial 'ab' is a prefix of 'abstract' and should not trigger type hints"
364+ ) ;
365+ }
366+
367+ #[ test]
368+ fn partial_str_after_public_is_type_hint ( ) {
369+ // `public str` — "str" is NOT a prefix of any declaration keyword,
370+ // so it should trigger type hints (e.g. offering `string`).
371+ let src = "<?php\n class Foo {\n public str\n }" ;
372+ let ctx = detect ( src, 2 , 14 ) . unwrap ( ) ;
373+ assert_eq ! ( ctx. partial, "str" ) ;
374+ }
375+
376+ #[ test]
377+ fn partial_us_after_public_is_type_hint ( ) {
378+ // `public Us` — "Us" is not a prefix of any declaration keyword,
379+ // so it should trigger type hints (e.g. offering `UserService`).
380+ let src = "<?php\n class Foo {\n public Us\n }" ;
381+ let ctx = detect ( src, 2 , 13 ) . unwrap ( ) ;
382+ assert_eq ! ( ctx. partial, "Us" ) ;
383+ }
384+
385+ #[ test]
386+ fn partial_fn_after_public_is_not_type_hint ( ) {
387+ // `public fn` — "fn" is an exact match for the keyword.
388+ let src = "<?php\n class Foo {\n public fn\n }" ;
389+ let ctx = detect ( src, 2 , 13 ) ;
390+ assert ! (
391+ ctx. is_none( ) ,
392+ "partial 'fn' matches declaration keyword and should not trigger type hints"
393+ ) ;
394+ }
395+
396+ #[ test]
397+ fn partial_use_after_public_is_not_type_hint ( ) {
398+ // Inside a class, `public use` — "use" is a declaration keyword (trait import).
399+ // Note: not valid PHP but the keyword check should still exclude it.
400+ let src = "<?php\n class Foo {\n public use\n }" ;
401+ let ctx = detect ( src, 2 , 14 ) ;
402+ assert ! (
403+ ctx. is_none( ) ,
404+ "partial 'use' matches declaration keyword and should not trigger type hints"
405+ ) ;
406+ }
407+
279408// ── Native types constant ───────────────────────────────────────
280409
281410#[ test]
0 commit comments