luaur_analysis/methods/
subtyping_is_covariant_with_deprecated.rs1use crate::enums::subtyping_suppression_policy::SubtypingSuppressionPolicy;
2use crate::enums::table_state::TableState;
3use crate::enums::type_field::TypeField;
4use crate::records::property_type::Property;
5use crate::records::property_type_path::Property as PathProperty;
6use crate::records::scope::Scope;
7use crate::records::subtyping::Subtyping;
8use crate::records::subtyping_environment::SubtypingEnvironment;
9use crate::records::subtyping_result::SubtypingResult;
10use crate::records::table_type::TableType;
11use crate::type_aliases::component::Component;
12use alloc::string::ToString;
13use alloc::vec::Vec;
14use luaur_common::FFlag;
15
16fn path_property(name: &str, is_read: bool) -> Component {
17 Component::Property(PathProperty {
18 name: name.to_string(),
19 is_read,
20 })
21}
22
23fn index_result_component() -> Component {
24 Component::TypeField(TypeField::IndexResult)
25}
26
27impl Subtyping {
28 pub fn is_covariant_with_deprecated(
29 &mut self,
30 env: &mut SubtypingEnvironment,
31 sub_table: &TableType,
32 super_table: &TableType,
33 force_covariant_test: bool,
34 scope: *mut Scope,
35 ) -> SubtypingResult {
36 let mut result = SubtypingResult {
37 is_subtype: true,
38 ..Default::default()
39 };
40
41 if sub_table.props.is_empty()
42 && sub_table.indexer.is_none()
43 && sub_table.state == TableState::Sealed
44 && super_table.indexer.is_some()
45 {
46 return SubtypingResult {
47 is_subtype: false,
48 ..Default::default()
49 };
50 }
51
52 for (name, super_prop) in &super_table.props {
53 let mut results = Vec::new();
54
55 if let Some(sub_prop) = sub_table.props.get(name) {
56 results.push(
57 self.is_covariant_with_subtyping_environment_property_property_string_bool_not_null_scope(
58 env,
59 sub_prop,
60 super_prop,
61 name,
62 force_covariant_test,
63 scope,
64 ),
65 );
66 } else if let Some(sub_indexer) = &sub_table.indexer {
67 let can_index_by_string = unsafe {
68 self.is_covariant_with_subtyping_environment_type_id_type_id_not_null_scope(
69 env,
70 (*self.builtin_types).stringType,
71 sub_indexer.index_type,
72 scope,
73 )
74 .is_subtype
75 };
76
77 if can_index_by_string {
78 if super_prop.is_shared() {
79 if FFlag::LuauReadOnlyIndexers.get() && sub_indexer.is_read_only {
80 let mut sr = SubtypingResult {
81 is_subtype: false,
82 ..Default::default()
83 };
84 sr.with_sub_component(index_result_component());
85 sr.with_super_component(path_property(name, true));
86 results.push(sr);
87 } else {
88 let mut sr = self
89 .is_invariant_with_subtyping_environment_sub_ty_super_ty_not_null_scope(
90 env,
91 sub_indexer.index_result_type,
92 super_prop.read_ty.unwrap(),
93 scope,
94 );
95 sr.with_sub_component(index_result_component());
96 sr.with_super_component(path_property(name, true));
97 results.push(sr);
98 }
99 } else {
100 if let Some(super_read_ty) = super_prop.read_ty {
101 let mut sr = self
102 .is_covariant_with_subtyping_environment_type_id_type_id_not_null_scope(
103 env,
104 sub_indexer.index_result_type,
105 super_read_ty,
106 scope,
107 );
108 sr.with_sub_component(index_result_component());
109 sr.with_super_component(path_property(name, true));
110 results.push(sr);
111 }
112
113 if let Some(super_write_ty) = super_prop.write_ty {
114 if FFlag::LuauReadOnlyIndexers.get() && sub_indexer.is_read_only {
115 let mut sr = SubtypingResult {
116 is_subtype: false,
117 ..Default::default()
118 };
119 sr.with_sub_component(index_result_component());
120 sr.with_super_component(path_property(name, false));
121 results.push(sr);
122 } else {
123 let mut sr = self
124 .is_contravariant_with_subtyping_environment_sub_ty_super_ty_not_null_scope(
125 env,
126 sub_indexer.index_result_type,
127 super_write_ty,
128 scope,
129 );
130 sr.with_sub_component(index_result_component());
131 sr.with_super_component(path_property(name, false));
132 results.push(sr);
133 }
134 }
135 }
136 }
137 } else if FFlag::LuauSubtypingMissingPropertiesAsNil.get() {
138 let nil_prop = unsafe { Property::readonly((*self.builtin_types).nilType) };
139 let mut sr =
140 self.is_covariant_with_subtyping_environment_property_property_string_bool_not_null_scope(
141 env,
142 &nil_prop,
143 super_prop,
144 name,
145 force_covariant_test,
146 scope,
147 );
148 sr.reasoning.clear();
149 results.push(sr);
150 }
151
152 if results.is_empty() {
153 return SubtypingResult {
154 is_subtype: false,
155 ..Default::default()
156 };
157 }
158
159 let is_subtype = results.iter().all(|sr| sr.is_subtype);
160
161 if result.is_subtype && !is_subtype {
162 for sr in results {
163 result.and_also(sr, SubtypingSuppressionPolicy::Any);
164 }
165 } else {
166 for sr in results {
167 result.and_also(sr, SubtypingSuppressionPolicy::All);
168 }
169 }
170 }
171
172 if let Some(super_indexer) = &super_table.indexer {
173 if let Some(sub_indexer) = &sub_table.indexer {
174 let indexer_result = if FFlag::LuauReadOnlyIndexers.get() {
175 self.is_covariant_with_subtyping_environment_table_indexer_table_indexer_not_null_scope(
176 env,
177 sub_indexer,
178 super_indexer,
179 scope,
180 )
181 } else {
182 self.is_invariant_with_subtyping_environment_sub_ty_super_ty_not_null_scope(
183 env,
184 *sub_indexer,
185 *super_indexer,
186 scope,
187 )
188 };
189 result.and_also(indexer_result, SubtypingSuppressionPolicy::All);
190 } else if sub_table.state != TableState::Sealed {
191 return SubtypingResult {
192 is_subtype: true,
193 ..Default::default()
194 };
195 } else {
196 return SubtypingResult {
197 is_subtype: false,
198 ..Default::default()
199 };
200 }
201 }
202
203 result
204 }
205}