Build completion items for a resolved class, filtered by access kind and visibility scope. - `Arrow` access: returns only non-static methods and properties. - `DoubleColon` access: returns only static methods, static properties, and constants. - `ParentDoubleColon` access: returns both static and non-static methods, static properties, and constants — but excludes private members. - `Other` access
(
target_class: &ClassInfo,
access_kind: AccessKind,
current_class_name: Option<&str>,
is_self_or_ancestor: bool,
uri: &str,
)
| 288 | /// `parent::__construct()`, `ClassName::__construct()` from within a |
| 289 | /// subclass). When `false`, magic methods are suppressed entirely. |
| 290 | pub(crate) fn build_completion_items( |
| 291 | target_class: &ClassInfo, |
| 292 | access_kind: AccessKind, |
| 293 | current_class_name: Option<&str>, |
| 294 | is_self_or_ancestor: bool, |
| 295 | uri: &str, |
| 296 | ) -> Vec<CompletionItem> { |
| 297 | // Determine whether we are inside the same class as the target. |
| 298 | let same_class = current_class_name.is_some_and(|name| name == target_class.name); |
| 299 | let mut items: Vec<CompletionItem> = Vec::new(); |
| 300 | |
| 301 | // Methods — filtered by static / instance, excluding magic methods |
| 302 | for method in &target_class.methods { |
| 303 | // `__construct` is meaningful to call explicitly via `::` when |
| 304 | // inside the same class or a subclass (e.g. |
| 305 | // `parent::__construct(...)`, `self::__construct()`). |
| 306 | // Outside of that relationship, magic methods are suppressed. |
| 307 | let is_constructor = method.name.eq_ignore_ascii_case("__construct"); |
| 308 | if is_magic_method(&method.name) { |
| 309 | let allow = is_constructor |
| 310 | && is_self_or_ancestor |
| 311 | && matches!( |
| 312 | access_kind, |
| 313 | AccessKind::DoubleColon | AccessKind::ParentDoubleColon |
| 314 | ); |
| 315 | if !allow { |
| 316 | continue; |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | // Visibility filtering: |
| 321 | // - private: only visible from within the same class |
| 322 | // - protected: visible from the same class or a subclass |
| 323 | // (we approximate by allowing when inside any class) |
| 324 | if method.visibility == Visibility::Private && !same_class { |
| 325 | continue; |
| 326 | } |
| 327 | if method.visibility == Visibility::Protected && !same_class && !is_self_or_ancestor { |
| 328 | continue; |
| 329 | } |
| 330 | |
| 331 | let include = match access_kind { |
| 332 | AccessKind::Arrow => !method.is_static, |
| 333 | // External `ClassName::` shows only static methods, but |
| 334 | // `__construct` is an exception — it's an instance method |
| 335 | // that is routinely called via `ClassName::__construct()` |
| 336 | // from within a subclass. |
| 337 | AccessKind::DoubleColon => method.is_static || is_constructor, |
| 338 | // `self::`, `static::`, and `parent::` show both static and |
| 339 | // non-static methods (PHP allows calling instance methods |
| 340 | // via `::` from within the class hierarchy). |
| 341 | AccessKind::ParentDoubleColon => true, |
| 342 | AccessKind::Other => true, |
| 343 | }; |
| 344 | if !include { |
| 345 | continue; |
| 346 | } |
| 347 |
no test coverage detected