@@ -94,10 +94,18 @@ impl Backend {
9494 /// (same-class access, e.g. `$this->`).
9595 /// - `Some(name)` where `name != target_class.name`: **public** and
9696 /// **protected** members are shown (the caller might be in a subclass).
97+ ///
98+ /// `is_self_or_ancestor` should be `true` when the cursor is inside the
99+ /// target class itself or inside a class that (transitively) extends the
100+ /// target. When `true`, `__construct` is offered for `::` access
101+ /// (e.g. `self::__construct()`, `parent::__construct()`,
102+ /// `ClassName::__construct()` from within a subclass). When `false`,
103+ /// magic methods are suppressed entirely.
97104 pub ( crate ) fn build_completion_items (
98105 target_class : & ClassInfo ,
99106 access_kind : AccessKind ,
100107 current_class_name : Option < & str > ,
108+ is_self_or_ancestor : bool ,
101109 ) -> Vec < CompletionItem > {
102110 // Determine whether we are inside the same class as the target.
103111 let same_class = current_class_name. is_some_and ( |name| name == target_class. name ) ;
@@ -108,12 +116,13 @@ impl Backend {
108116 // Methods — filtered by static / instance, excluding magic methods
109117 for method in & target_class. methods {
110118 // `__construct` is meaningful to call explicitly via `::` or
111- // `parent::` (e.g. `parent::__construct(...)` in a child class),
112- // so we only suppress it for `->` access. All other magic
113- // methods are always suppressed.
119+ // `parent::` when inside the same class or a subclass
120+ // (e.g. `parent::__construct(...)`, `self::__construct()`).
121+ // Outside of that relationship, magic methods are suppressed.
114122 let is_constructor = method. name . eq_ignore_ascii_case ( "__construct" ) ;
115123 if Self :: is_magic_method ( & method. name ) {
116124 let allow = is_constructor
125+ && is_self_or_ancestor
117126 && matches ! (
118127 access_kind,
119128 AccessKind :: DoubleColon | AccessKind :: ParentDoubleColon
@@ -235,6 +244,19 @@ impl Backend {
235244 }
236245 }
237246
247+ // `::class` keyword — returns the fully qualified class name as a string.
248+ // Available on any class, interface, or enum via `::` access.
249+ if access_kind == AccessKind :: DoubleColon || access_kind == AccessKind :: ParentDoubleColon {
250+ items. push ( CompletionItem {
251+ label : "class" . to_string ( ) ,
252+ kind : Some ( CompletionItemKind :: KEYWORD ) ,
253+ detail : Some ( "class-string" . to_string ( ) ) ,
254+ insert_text : Some ( "class" . to_string ( ) ) ,
255+ filter_text : Some ( "class" . to_string ( ) ) ,
256+ ..CompletionItem :: default ( )
257+ } ) ;
258+ }
259+
238260 // Sort all items alphabetically (case-insensitive) and assign
239261 // sort_text so the editor preserves this ordering.
240262 items. sort_by ( |a, b| {
0 commit comments