Skip to content

Commit d7409e9

Browse files
committed
fix(analyzer): track symbol references from partial applications
Signed-off-by: azjezz <azjezz@protonmail.com>
1 parent b17307e commit d7409e9

File tree

4 files changed

+107
-26
lines changed

4 files changed

+107
-26
lines changed

crates/analyzer/src/expression/partial_application/function_partial_application.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Cow;
22

33
use mago_atom::AtomMap;
4+
use mago_atom::ascii_lowercase_atom;
45
use mago_atom::atom;
56
use mago_codex::identifier::function_like::FunctionLikeIdentifier;
67
use mago_codex::ttype::TType;
@@ -171,6 +172,14 @@ fn resolve_function_callable_types<'ctx, 'arena, 'artifacts>(
171172
return Ok(vec![]);
172173
};
173174

175+
if let FunctionLikeIdentifier::Function(function_name) = identifier {
176+
artifacts.symbol_references.add_reference_to_symbol(
177+
&block_context.scope,
178+
ascii_lowercase_atom(function_name.as_ref()),
179+
false,
180+
);
181+
}
182+
174183
return Ok(vec![Cow::Owned(TCallable::Alias(identifier))]);
175184
}
176185

crates/analyzer/src/expression/partial_application/method_partial_application.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use mago_atom::AtomMap;
2+
use mago_atom::ascii_lowercase_atom;
23
use mago_codex::identifier::function_like::FunctionLikeIdentifier;
34
use mago_codex::ttype::atomic::TAtomic;
45
use mago_codex::ttype::atomic::callable::TCallable;
@@ -34,22 +35,29 @@ impl<'ast, 'arena> Analyzable<'ast, 'arena> for MethodPartialApplication<'arena>
3435
let method_resolution =
3536
resolve_method_targets(context, block_context, artifacts, self.object, &self.method, false, self.span())?;
3637

38+
let mut identifiers = vec![];
39+
for resolved_method in &method_resolution.resolved_methods {
40+
let class_name = ascii_lowercase_atom(resolved_method.method_identifier.get_class_name().as_ref());
41+
let method_name = *resolved_method.method_identifier.get_method_name();
42+
artifacts.symbol_references.add_reference_to_class_member(
43+
&block_context.scope,
44+
(class_name, method_name),
45+
false,
46+
);
47+
48+
identifiers.push(FunctionLikeIdentifier::Method(
49+
*resolved_method.method_identifier.get_class_name(),
50+
*resolved_method.method_identifier.get_method_name(),
51+
));
52+
}
53+
3754
let resulting_type = if self.argument_list.is_first_class_callable() {
38-
let callable_types: Vec<TAtomic> = method_resolution
39-
.resolved_methods
40-
.into_iter()
41-
.map(|resolved_method| {
42-
TAtomic::Callable(TCallable::Alias(FunctionLikeIdentifier::Method(
43-
*resolved_method.method_identifier.get_class_name(),
44-
*resolved_method.method_identifier.get_method_name(),
45-
)))
46-
})
47-
.collect();
48-
49-
if callable_types.is_empty() {
55+
if identifiers.is_empty() {
5056
if method_resolution.has_invalid_target { get_never() } else { get_mixed_closure() }
5157
} else {
52-
TUnion::from_vec(callable_types)
58+
TUnion::from_vec(
59+
identifiers.into_iter().map(|identifier| TAtomic::Callable(TCallable::Alias(identifier))).collect(),
60+
)
5361
}
5462
} else {
5563
let mut closure_types = Vec::new();

crates/analyzer/src/expression/partial_application/static_method_partial_application.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use mago_atom::AtomMap;
2+
use mago_atom::ascii_lowercase_atom;
23
use mago_codex::identifier::function_like::FunctionLikeIdentifier;
34
use mago_codex::ttype::atomic::TAtomic;
45
use mago_codex::ttype::atomic::callable::TCallable;
@@ -33,22 +34,29 @@ impl<'ast, 'arena> Analyzable<'ast, 'arena> for StaticMethodPartialApplication<'
3334
let method_resolution =
3435
resolve_static_method_targets(context, block_context, artifacts, self.class, &self.method, self.span())?;
3536

37+
let mut identifiers = vec![];
38+
for resolved_method in &method_resolution.resolved_methods {
39+
let class_name = ascii_lowercase_atom(resolved_method.classname.as_ref());
40+
let method_name = *resolved_method.method_identifier.get_method_name();
41+
artifacts.symbol_references.add_reference_to_class_member(
42+
&block_context.scope,
43+
(class_name, method_name),
44+
false,
45+
);
46+
47+
identifiers.push(FunctionLikeIdentifier::Method(
48+
*resolved_method.method_identifier.get_class_name(),
49+
*resolved_method.method_identifier.get_method_name(),
50+
));
51+
}
52+
3653
let resulting_type = if self.argument_list.is_first_class_callable() {
37-
let callable_types: Vec<TAtomic> = method_resolution
38-
.resolved_methods
39-
.into_iter()
40-
.map(|resolved_method| {
41-
TAtomic::Callable(TCallable::Alias(FunctionLikeIdentifier::Method(
42-
resolved_method.classname,
43-
*resolved_method.method_identifier.get_method_name(),
44-
)))
45-
})
46-
.collect();
47-
48-
if callable_types.is_empty() {
54+
if identifiers.is_empty() {
4955
if method_resolution.has_invalid_target { get_never() } else { get_mixed_closure() }
5056
} else {
51-
TUnion::from_vec(callable_types)
57+
TUnion::from_vec(
58+
identifiers.into_iter().map(|identifier| TAtomic::Callable(TCallable::Alias(identifier))).collect(),
59+
)
5260
}
5361
} else {
5462
let mut closure_types = Vec::new();

crates/analyzer/tests/cases/unused_method.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,59 @@ public function unused(): void
5050
{
5151
}
5252
}
53+
54+
class UsedViaFirstClassCallable
55+
{
56+
private function helper(string $name): string
57+
{
58+
return "Hello, $name";
59+
}
60+
61+
public function main(): void
62+
{
63+
$closure = $this->helper(...);
64+
$closure("World");
65+
}
66+
}
67+
68+
class UsedViaPartialApplication
69+
{
70+
private function greet(string $greeting, string $name): string
71+
{
72+
return "$greeting, $name";
73+
}
74+
75+
public function main(): void
76+
{
77+
$sayHello = $this->greet("Hello", ?);
78+
$sayHello("World");
79+
}
80+
}
81+
82+
class UsedStaticViaFirstClassCallable
83+
{
84+
private static function helper(int $x): int
85+
{
86+
return $x * 2;
87+
}
88+
89+
public function main(): void
90+
{
91+
$closure = self::helper(...);
92+
$closure(5);
93+
}
94+
}
95+
96+
class UsedStaticViaPartialApplication
97+
{
98+
private static function add(int $a, int $b): int
99+
{
100+
return $a + $b;
101+
}
102+
103+
public function main(): void
104+
{
105+
$addFive = self::add(5, ?);
106+
$addFive(10);
107+
}
108+
}

0 commit comments

Comments
 (0)