Skip to content

Commit 6ec581b

Browse files
committed
feat(analyzer): add a method return type provider for UnitEnum::cases() method
closes #735 Signed-off-by: azjezz <[email protected]>
1 parent 6fd3b1b commit 6ec581b

File tree

6 files changed

+93
-0
lines changed

6 files changed

+93
-0
lines changed

crates/analyzer/src/plugin/libraries/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub fn register_library_providers(registry: &mut PluginRegistry) {
1818
registry.register_function_provider(stdlib::array::ArrayMergeProvider);
1919
registry.register_function_provider(stdlib::array::CompactProvider);
2020
registry.register_method_provider(stdlib::closure::ClosureGetCurrentProvider);
21+
registry.register_method_provider(stdlib::r#enum::EnumCasesProvider);
2122

2223
registry.register_function_provider(psl::type_::ShapeProvider);
2324
registry.register_function_provider(psl::type_::OptionalProvider);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//! UnitEnum::cases() return type provider.
2+
3+
use mago_codex::ttype::atomic::TAtomic;
4+
use mago_codex::ttype::atomic::array::TArray;
5+
use mago_codex::ttype::atomic::array::list::TList;
6+
use mago_codex::ttype::atomic::object::TObject;
7+
use mago_codex::ttype::atomic::object::r#enum::TEnum;
8+
use mago_codex::ttype::union::TUnion;
9+
10+
use crate::plugin::context::InvocationInfo;
11+
use crate::plugin::context::ProviderContext;
12+
use crate::plugin::provider::Provider;
13+
use crate::plugin::provider::ProviderMeta;
14+
use crate::plugin::provider::method::MethodReturnTypeProvider;
15+
use crate::plugin::provider::method::MethodTarget;
16+
17+
static META: ProviderMeta =
18+
ProviderMeta::new("php::enum::cases", "UnitEnum::cases", "Returns non-empty-list for enums with at least one case");
19+
20+
// Use wildcard for class since all enums implement UnitEnum
21+
static TARGETS: [MethodTarget; 1] = [MethodTarget::any_class("cases")];
22+
23+
/// Provider for the `UnitEnum::cases()` method.
24+
///
25+
/// Returns `non-empty-list<EnumType>` for enums with at least one case,
26+
/// or `list<EnumType>` for enums with no cases (edge case).
27+
#[derive(Default)]
28+
pub struct EnumCasesProvider;
29+
30+
impl Provider for EnumCasesProvider {
31+
fn meta() -> &'static ProviderMeta {
32+
&META
33+
}
34+
}
35+
36+
impl MethodReturnTypeProvider for EnumCasesProvider {
37+
fn targets() -> &'static [MethodTarget] {
38+
&TARGETS
39+
}
40+
41+
fn get_return_type(
42+
&self,
43+
_context: &ProviderContext<'_, '_, '_>,
44+
_class_name: &str,
45+
_method_name: &str,
46+
invocation_info: &InvocationInfo<'_, '_, '_>,
47+
) -> Option<TUnion> {
48+
let class_metadata = invocation_info.invocation.target.get_method_context()?.class_like_metadata;
49+
50+
if !class_metadata.kind.is_enum() {
51+
return None;
52+
}
53+
54+
let enum_type = TUnion::from_atomic(TAtomic::Object(TObject::Enum(TEnum {
55+
name: class_metadata.original_name,
56+
case: None,
57+
})));
58+
59+
if !class_metadata.enum_cases.is_empty() {
60+
Some(TUnion::from_atomic(TAtomic::Array(TArray::List(TList::new_non_empty(Box::new(enum_type))))))
61+
} else {
62+
Some(TUnion::from_atomic(TAtomic::Array(TArray::List(TList::new(Box::new(enum_type))))))
63+
}
64+
}
65+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//! PHP Enum method providers.
2+
3+
mod cases;
4+
5+
pub use cases::EnumCasesProvider;

crates/analyzer/src/plugin/libraries/stdlib/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
pub mod array;
44
pub mod closure;
5+
pub mod r#enum;
56
pub mod json;
67
pub mod random;
78
pub mod spl;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @param non-empty-list $choices
7+
*/
8+
function countNonEmptyList(array $choices): int
9+
{
10+
return count($choices);
11+
}
12+
13+
enum MyCases: string
14+
{
15+
case FIRST = 'first';
16+
case SECOND = 'second';
17+
case THIRD = 'third';
18+
}
19+
20+
countNonEmptyList(MyCases::cases());

crates/analyzer/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ test_case!(issue_728_symfony_reference);
443443
test_case!(issue_729);
444444
test_case!(issue_731);
445445
test_case!(issue_733);
446+
test_case!(issue_735);
446447
test_case!(issue_736);
447448
test_case!(issue_737);
448449
test_case!(issue_739);

0 commit comments

Comments
 (0)