@@ -559,3 +559,38 @@ async fn test_multiline_chain_same_line_after_closure_arg_close() {
559559 "Should offer Collection::all() after same-line closure close, got: {names:?}"
560560 ) ;
561561}
562+
563+ #[ tokio:: test]
564+ async fn test_multiline_chain_same_line_after_bracket_close_does_not_misresolve ( ) {
565+ // Bracket depth (`[`/`]`) is not tracked by the backward balance scan
566+ // used to reconstruct the base expression, so a chain operator that
567+ // follows a closing `]` on its own line must not be treated as a
568+ // same-line continuation: doing so would drop everything before the
569+ // bracket close (e.g. the `$this->items[` base) and hand the resolver
570+ // a malformed expression instead of the real receiver. Falling back to
571+ // "no completions" is correct here; resolving to the wrong receiver's
572+ // members would not be.
573+ let backend = create_test_backend ( ) ;
574+ let uri = Url :: parse ( "file:///multiline_bracket_same_line.php" ) . unwrap ( ) ;
575+ let text = concat ! (
576+ "<?php\n " ,
577+ "class Box {\n " ,
578+ " public function open(): string { return ''; }\n " ,
579+ "}\n " ,
580+ "class Container {\n " ,
581+ " /** @var array<string, Box> */\n " ,
582+ " public array $items = [];\n " ,
583+ " public function run(): void {\n " ,
584+ " $this->items[\n " ,
585+ " 'key'\n " ,
586+ " ]->o\n " ,
587+ " }\n " ,
588+ "}\n " ,
589+ ) ;
590+
591+ let names = complete_at ( & backend, & uri, text, 10 , 12 ) . await ;
592+ assert ! (
593+ !names. iter( ) . any( |n| n. starts_with( "open(" ) ) ,
594+ "Should not resolve Box::open() from a malformed collapsed expression, got: {names:?}"
595+ ) ;
596+ }
0 commit comments