@@ -7,7 +7,9 @@ use tower_lsp::lsp_types::*;
77
88use crate :: Backend ;
99use crate :: types:: * ;
10- use crate :: util:: skip_balanced_parens_back;
10+ use crate :: util:: {
11+ check_new_keyword_before, extract_new_expression_inside_parens, skip_balanced_parens_back,
12+ } ;
1113
1214impl Backend {
1315 /// Detect the access operator before the cursor position and extract
@@ -68,6 +70,8 @@ impl Backend {
6870 /// - `app()->` (function call)
6971 /// - `$this->getService()->` (method call chain)
7072 /// - `ClassName::make()->` (static method call)
73+ /// - `new ClassName()->` (instantiation, PHP 8.4+)
74+ /// - `(new ClassName())->` (parenthesized instantiation)
7175 fn extract_arrow_subject ( chars : & [ char ] , arrow_pos : usize ) -> String {
7276 // Position just before the `->`
7377 let end = arrow_pos;
@@ -78,8 +82,9 @@ impl Backend {
7882 i -= 1 ;
7983 }
8084
81- // ── Function / method call: detect `)` before the operator ──
82- // e.g. `app()->`, `$this->getService()->`, `Class::make()->`
85+ // ── Function / method call or `new` expression: detect `)` ──
86+ // e.g. `app()->`, `$this->getService()->`, `Class::make()->`,
87+ // `new Foo()->`, `(new Foo())->`
8388 if i > 0
8489 && chars[ i - 1 ] == ')'
8590 && let Some ( call_subject) = Self :: extract_call_subject ( chars, i)
@@ -128,6 +133,7 @@ impl Backend {
128133 /// - `"app()"` for a standalone function call
129134 /// - `"$this->getService()"` for an instance method call
130135 /// - `"ClassName::make()"` for a static method call
136+ /// - `"ClassName"` for `new ClassName()` instantiation
131137 fn extract_call_subject ( chars : & [ char ] , paren_end : usize ) -> Option < String > {
132138 let open = skip_balanced_parens_back ( chars, paren_end) ?;
133139 if open == 0 {
@@ -136,15 +142,24 @@ impl Backend {
136142
137143 // Read the function / method name before `(`
138144 let mut i = open;
139- while i > 0 && ( chars[ i - 1 ] . is_alphanumeric ( ) || chars[ i - 1 ] == '_' ) {
145+ while i > 0
146+ && ( chars[ i - 1 ] . is_alphanumeric ( ) || chars[ i - 1 ] == '_' || chars[ i - 1 ] == '\\' )
147+ {
140148 i -= 1 ;
141149 }
142150 if i == open {
143- // No identifier before `(` — can't resolve
144- return None ;
151+ // No identifier before `(` — check if the contents inside the
152+ // balanced parens form a `(new ClassName(...))` expression.
153+ return extract_new_expression_inside_parens ( chars, open, paren_end) ;
145154 }
146155 let func_name: String = chars[ i..open] . iter ( ) . collect ( ) ;
147156
157+ // ── `new ClassName()` instantiation ──
158+ // Check if the `new` keyword immediately precedes the class name.
159+ if let Some ( class_name) = check_new_keyword_before ( chars, i, & func_name) {
160+ return Some ( class_name) ;
161+ }
162+
148163 // Check what precedes the function name to determine the kind of
149164 // call expression.
150165
0 commit comments