Extract parameter information from a method's parameter list. When `content` is provided, default value expressions are extracted from the source text using AST span offsets. Pass `None` when the source text is not available (the `default_value` field will be `None` for every parameter in that case). When `php_version` is `Some`, parameters annotated with `#[PhpStormStubsElementAvailable]` whos
(
parameter_list: &FunctionLikeParameterList,
content: Option<&str>,
php_version: Option<PhpVersion>,
doc_ctx: Option<&DocblockCtx<'_>>,
)
| 893 | /// target version are filtered out. When `None`, all parameters are |
| 894 | /// included. |
| 895 | pub(crate) fn extract_parameters( |
| 896 | parameter_list: &FunctionLikeParameterList, |
| 897 | content: Option<&str>, |
| 898 | php_version: Option<PhpVersion>, |
| 899 | doc_ctx: Option<&DocblockCtx<'_>>, |
| 900 | ) -> Vec<ParameterInfo> { |
| 901 | parameter_list |
| 902 | .parameters |
| 903 | .iter() |
| 904 | .filter(|param| { |
| 905 | // When a PHP version is configured, skip parameters that are |
| 906 | // not available for that version. |
| 907 | if let Some(ver) = php_version |
| 908 | && let Some(ctx) = doc_ctx |
| 909 | { |
| 910 | is_param_available_for_version(param, ctx, ver) |
| 911 | } else { |
| 912 | true |
| 913 | } |
| 914 | }) |
| 915 | .map(|param| { |
| 916 | let name = atom(param.variable.name); |
| 917 | let is_variadic = param.ellipsis.is_some(); |
| 918 | let is_reference = param.ampersand.is_some(); |
| 919 | let has_default = param.default_value.is_some(); |
| 920 | let is_required = !has_default && !is_variadic; |
| 921 | |
| 922 | let native_type_hint = param.hint.as_ref().map(|h| extract_hint_type(h)); |
| 923 | |
| 924 | // Check for a #[LanguageLevelTypeAware] override on the |
| 925 | // parameter. When present, it replaces the native type hint |
| 926 | // with the version-appropriate type string. |
| 927 | let type_hint = if let Some(ver) = php_version |
| 928 | && let Some(ctx) = doc_ctx |
| 929 | { |
| 930 | extract_language_level_type_for_param(param, ctx, ver) |
| 931 | .or_else(|| native_type_hint.clone()) |
| 932 | } else { |
| 933 | native_type_hint.clone() |
| 934 | }; |
| 935 | |
| 936 | let default_value = content.and_then(|src| { |
| 937 | let dv = param.default_value.as_ref()?; |
| 938 | let span = dv.value.span(); |
| 939 | let start = span.start.offset as usize; |
| 940 | let end = span.end.offset as usize; |
| 941 | let raw = src.get(start..end)?.trim().to_string(); |
| 942 | // Resolve `ClassName::class` to its FQN so that |
| 943 | // downstream template substitution can find the class. |
| 944 | if let Some(class_name) = raw.strip_suffix("::class") { |
| 945 | let fqn = resolve_default_class_name(class_name, doc_ctx); |
| 946 | Some(format!("{}::class", fqn)) |
| 947 | } else { |
| 948 | Some(raw) |
| 949 | } |
| 950 | }); |
| 951 | |
| 952 | ParameterInfo { |
no test coverage detected