1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use mago_span::Span;
use crate::ttype::union::TUnion;
/// Contains metadata associated with a specific type instance within the type system.
///
/// This struct combines the core type information (`TUnion`) with contextual details
/// about *how* and *where* this type information was determined or declared in the source code
/// or related documentation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct TypeMetadata {
/// The specific location (span) in the source code or documentation
/// that this type metadata corresponds to.
///
/// This could be:
/// - The span of a type annotation (e.g., `: string`).
/// - The span of an expression whose type was inferred (e.g., `$x = 10` -> span of `10`).
/// - The span of a type mentioned in a documentation block (e.g., `@param int` -> span of `int`).
pub span: Span,
/// The core representation of the type itself.
pub type_union: TUnion,
/// Distinguishes whether this type information originated from analyzing
/// executable code constructs (e.g., type declarations, assignments)
/// or from documentation blocks.
///
/// - `true` if the type information was extracted from a docblock comment.
/// - `false` if the type information came from actual code analysis.
pub from_docblock: bool,
/// Indicates whether this type was explicitly declared in the source
/// or deduced ("inferred") by the type checker based on context.
///
/// - `true` if the type checker inferred this type (e.g., from a variable initialization like `$x = 10;`).
/// - `false` if the type was explicitly written by the user (e.g., `int $x;`).
pub inferred: bool,
}
impl TypeMetadata {
/// Creates new `TypeMetadata` for an explicitly declared type from code.
///
/// This is a convenience constructor assuming the common case where a type
/// is directly specified in the code, and wasn't just inferred or from a docblock.
///
/// # Arguments
///
/// * `type_union`: The core type information (`TUnion`).
/// * `span`: The source code location associated with this type.
///
/// # Returns
///
/// A new `TypeMetadata` instance with `is_nullable`, `from_docblock`, and `inferred` set to `false`.
#[must_use]
pub fn new(type_union: TUnion, span: Span) -> Self {
Self { span, type_union, from_docblock: false, inferred: false }
}
/// Creates new `TypeMetadata` for a type extracted from a documentation block.
///
/// This constructor is used when type information is sourced from
/// docblock comments rather than executable code.
///
/// # Arguments
///
/// * `type_union`: The core type information (`TUnion`).
/// * `span`: The source code location associated with this docblock type.
///
/// # Returns
///
/// A new `TypeMetadata` instance with `from_docblock` set to `true` and `inferred` set to `false`.
#[must_use]
pub fn from_docblock(type_union: TUnion, span: Span) -> Self {
Self { span, type_union, from_docblock: true, inferred: false }
}
/// Creates a new `TypeMetadata` by applying a function to the inner `TUnion`.
///
/// This allows transforming the core type while preserving the surrounding metadata.
///
/// # Arguments
///
/// * `f`: A function that takes the current `TUnion` and returns a new `TUnion`.
///
/// # Returns
///
/// A new `TypeMetadata` instance with the transformed `TUnion` and the same metadata flags and span.
#[must_use]
pub fn map_type_union<F>(self, f: F) -> Self
where
F: FnOnce(TUnion) -> TUnion,
{
Self {
span: self.span,
type_union: f(self.type_union),
from_docblock: self.from_docblock,
inferred: self.inferred,
}
}
}