mago_analyzer/plugin/provider/assertion.rs
1//! Assertion providers for function and method calls.
2//!
3//! These providers allow plugins to specify additional type assertions that
4//! should be applied after a function or method call.
5
6use std::collections::BTreeMap;
7
8use mago_algebra::assertion_set::Conjunction;
9use mago_atom::Atom;
10use mago_codex::assertion::Assertion;
11
12use crate::plugin::context::InvocationInfo;
13use crate::plugin::context::ProviderContext;
14use crate::plugin::provider::Provider;
15use crate::plugin::provider::function::FunctionTarget;
16use crate::plugin::provider::method::MethodTarget;
17
18/// Assertions to apply after an invocation.
19///
20/// Contains maps from variable names to assertion sets for:
21/// - `immediate`: assertions that apply unconditionally after the call
22/// - `if_true`: assertions that hold when the call returns truthy
23/// - `if_false`: assertions that hold when the call returns falsy
24#[derive(Debug, Clone, Default)]
25pub struct InvocationAssertions {
26 /// Assertions that apply unconditionally after the invocation.
27 ///
28 /// Keys are variable names (e.g., "$x"), values are assertion sets.
29 pub type_assertions: BTreeMap<Atom, Conjunction<Assertion>>,
30
31 /// Assertions that hold when the invocation returns truthy.
32 ///
33 /// Keys are variable names (e.g., "$x"), values are assertion sets.
34 pub if_true: BTreeMap<Atom, Conjunction<Assertion>>,
35
36 /// Assertions that hold when the invocation returns falsy.
37 ///
38 /// Keys are variable names (e.g., "$x"), values are assertion sets.
39 pub if_false: BTreeMap<Atom, Conjunction<Assertion>>,
40}
41
42impl InvocationAssertions {
43 /// Create new empty assertions.
44 #[inline]
45 #[must_use]
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 /// Check if there are any assertions.
51 #[inline]
52 #[must_use]
53 pub fn is_empty(&self) -> bool {
54 self.type_assertions.is_empty() && self.if_true.is_empty() && self.if_false.is_empty()
55 }
56
57 /// Add an immediate assertion for a variable.
58 pub fn add_immediate(&mut self, variable: Atom, assertions: Conjunction<Assertion>) {
59 self.type_assertions.insert(variable, assertions);
60 }
61
62 /// Add an if-true assertion for a variable.
63 pub fn add_if_true(&mut self, variable: Atom, assertions: Conjunction<Assertion>) {
64 self.if_true.insert(variable, assertions);
65 }
66
67 /// Add an if-false assertion for a variable.
68 pub fn add_if_false(&mut self, variable: Atom, assertions: Conjunction<Assertion>) {
69 self.if_false.insert(variable, assertions);
70 }
71}
72
73/// Provider for getting additional assertions from function calls.
74///
75/// This allows plugins to specify type narrowing for calls like:
76/// - `assert($x instanceof Foo)` - narrows `$x` to `Foo` after the call
77/// - `Assert::assertIsString($x)` - narrows `$x` to `string` after the call
78pub trait FunctionAssertionProvider: Provider {
79 /// The functions this provider handles.
80 fn targets() -> FunctionTarget
81 where
82 Self: Sized;
83
84 /// Get assertions for the invocation.
85 ///
86 /// Returns `Some(assertions)` if this provider has assertions to add,
87 /// `None` otherwise.
88 fn get_assertions(
89 &self,
90 context: &ProviderContext<'_, '_, '_>,
91 invocation: &InvocationInfo<'_, '_, '_>,
92 ) -> Option<InvocationAssertions>;
93}
94
95/// Provider for getting additional assertions from method calls.
96///
97/// This allows plugins to specify type narrowing for method calls like:
98/// - `$validator->isString($x)` - narrows `$x` to `string` if returns true
99/// - `Assert::assertInstanceOf(Foo::class, $x)` - narrows `$x` to `Foo`
100pub trait MethodAssertionProvider: Provider {
101 /// The methods this provider handles.
102 fn targets() -> &'static [MethodTarget]
103 where
104 Self: Sized;
105
106 /// Get assertions for the method invocation.
107 ///
108 /// Returns `Some(assertions)` if this provider has assertions to add,
109 /// `None` otherwise.
110 fn get_assertions(
111 &self,
112 context: &ProviderContext<'_, '_, '_>,
113 class_name: &str,
114 method_name: &str,
115 invocation: &InvocationInfo<'_, '_, '_>,
116 ) -> Option<InvocationAssertions>;
117}