MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / build_generic_subs

Function build_generic_subs

src/inheritance.rs:1298–1358  ·  view source on GitHub ↗

Build a substitution map from a class's template parameters and concrete type arguments. Handles right-alignment when fewer arguments than template parameters are provided (see [`apply_generic_args`] for details on the heuristic). Returns an empty map when no substitutions can be made (e.g. when `template_params` or `type_args` is empty).

(
    class: &ClassInfo,
    type_args: &[PhpType],
)

Source from the content-addressed store, hash-verified

1296/// Returns an empty map when no substitutions can be made (e.g. when
1297/// `template_params` or `type_args` is empty).
1298pub(crate) fn build_generic_subs(
1299 class: &ClassInfo,
1300 type_args: &[PhpType],
1301) -> HashMap<String, PhpType> {
1302 if class.template_params.is_empty() || type_args.is_empty() {
1303 return HashMap::new();
1304 }
1305
1306 // When fewer type arguments are provided than template parameters,
1307 // right-align the args so that trailing (value) params get bound
1308 // and leading key-like params stay unbound. This handles the
1309 // common PHP pattern of writing `Collection<Model>` instead of
1310 // `Collection<int, Model>` — the single arg should bind to
1311 // `TValue`/`TModel`, not `TKey`.
1312 //
1313 // The heuristic only activates when every skipped leading param
1314 // has an `array-key` (or `int` / `string`) bound, which is the
1315 // universal convention for collection key parameters.
1316 let offset = if type_args.len() < class.template_params.len() {
1317 let skip = class.template_params.len() - type_args.len();
1318 let all_skipped_are_key_like = class.template_params[..skip].iter().all(|param| {
1319 class
1320 .template_param_bounds
1321 .get(param)
1322 .is_some_and(is_key_like_bound)
1323 });
1324 if all_skipped_are_key_like { skip } else { 0 }
1325 } else {
1326 0
1327 };
1328
1329 let mut subs = HashMap::new();
1330 for (i, param_name) in class.template_params.iter().enumerate() {
1331 if i < offset {
1332 // Skipped (right-aligned) params: fall back to their
1333 // declared upper bound or `mixed` so the raw template
1334 // name never leaks into downstream consumers.
1335 let fallback = class
1336 .template_param_bounds
1337 .get(param_name)
1338 .cloned()
1339 .unwrap_or_else(PhpType::mixed);
1340 subs.insert(param_name.to_string(), fallback);
1341 continue;
1342 }
1343 if let Some(arg) = type_args.get(i - offset) {
1344 subs.insert(param_name.to_string(), arg.clone());
1345 } else {
1346 // Unbound param (more template params than type args and
1347 // right-alignment didn't apply): use upper bound or `mixed`.
1348 let fallback = class
1349 .template_param_bounds
1350 .get(param_name)
1351 .cloned()
1352 .unwrap_or_else(PhpType::mixed);
1353 subs.insert(param_name.to_string(), fallback);
1354 }
1355 }

Calls 6

iterMethod · 0.80
cloneMethod · 0.80
is_emptyMethod · 0.45
allMethod · 0.45
getMethod · 0.45
insertMethod · 0.45

Tested by

no test coverage detected