Skip to content

Commit 466376f

Browse files
committed
Fix visibility in global context
1 parent d92708f commit 466376f

3 files changed

Lines changed: 649 additions & 15 deletions

File tree

src/completion/builder.rs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,31 @@ impl Backend {
7676
format!("{}({}){}", method.name, params.join(", "), ret)
7777
}
7878

79-
/// Build completion items for a resolved class, filtered by access kind.
79+
/// Build completion items for a resolved class, filtered by access kind
80+
/// and visibility scope.
8081
///
8182
/// - `Arrow` access: returns only non-static methods and properties.
8283
/// - `DoubleColon` access: returns only static methods, static properties, and constants.
8384
/// - `ParentDoubleColon` access: returns both static and non-static methods,
8485
/// static properties, and constants — but excludes private members.
8586
/// - `Other` access: returns all members.
87+
///
88+
/// Visibility filtering based on `current_class_name`:
89+
/// - `None` (top-level code): only **public** members are shown.
90+
/// - `Some(name)` where `name == target_class.name`: all members are shown
91+
/// (same-class access, e.g. `$this->`).
92+
/// - `Some(name)` where `name != target_class.name`: **public** and
93+
/// **protected** members are shown (the caller might be in a subclass).
8694
pub(crate) fn build_completion_items(
8795
target_class: &ClassInfo,
8896
access_kind: AccessKind,
97+
current_class_name: Option<&str>,
8998
) -> Vec<CompletionItem> {
99+
// Determine whether we are inside the same class as the target.
100+
let same_class = current_class_name
101+
.is_some_and(|name| name == target_class.name);
102+
// Inside *some* class (possibly a subclass) — show protected.
103+
let in_class = current_class_name.is_some();
90104
let mut items: Vec<CompletionItem> = Vec::new();
91105

92106
// Methods — filtered by static / instance, excluding magic methods
@@ -107,10 +121,14 @@ impl Backend {
107121
}
108122
}
109123

110-
// parent:: excludes private members
111-
if access_kind == AccessKind::ParentDoubleColon
112-
&& method.visibility == Visibility::Private
113-
{
124+
// Visibility filtering:
125+
// - private: only visible from within the same class
126+
// - protected: visible from the same class or a subclass
127+
// (we approximate by allowing when inside any class)
128+
if method.visibility == Visibility::Private && !same_class {
129+
continue;
130+
}
131+
if method.visibility == Visibility::Protected && !same_class && !in_class {
114132
continue;
115133
}
116134

@@ -142,10 +160,10 @@ impl Backend {
142160

143161
// Properties — filtered by static / instance
144162
for property in &target_class.properties {
145-
// parent:: excludes private members
146-
if access_kind == AccessKind::ParentDoubleColon
147-
&& property.visibility == Visibility::Private
148-
{
163+
if property.visibility == Visibility::Private && !same_class {
164+
continue;
165+
}
166+
if property.visibility == Visibility::Protected && !same_class && !in_class {
149167
continue;
150168
}
151169

@@ -191,10 +209,10 @@ impl Backend {
191209
|| access_kind == AccessKind::Other
192210
{
193211
for constant in &target_class.constants {
194-
// parent:: excludes private members
195-
if access_kind == AccessKind::ParentDoubleColon
196-
&& constant.visibility == Visibility::Private
197-
{
212+
if constant.visibility == Visibility::Private && !same_class {
213+
continue;
214+
}
215+
if constant.visibility == Visibility::Protected && !same_class && !in_class {
198216
continue;
199217
}
200218

src/server.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,12 @@ impl LanguageServer for Backend {
361361
// deduplicating by label so ambiguous variables show
362362
// the union of all possible members.
363363
let mut all_items: Vec<CompletionItem> = Vec::new();
364+
let current_class_name =
365+
current_class.map(|cc| cc.name.as_str());
364366
for target_class in &candidates {
365367
let merged =
366368
Self::resolve_class_with_inheritance(target_class, &class_loader);
367-
let items = Self::build_completion_items(&merged, effective_access);
369+
let items = Self::build_completion_items(&merged, effective_access, current_class_name);
368370
for item in items {
369371
if !all_items
370372
.iter()

0 commit comments

Comments
 (0)