use mago_atom::Atom;
use mago_atom::atom;
use mago_names::scope::NamespaceScope;
use mago_reporting::Annotation;
use mago_reporting::Issue;
use mago_span::HasSpan;
use mago_syntax::ast::ClassLikeConstant;
use crate::issue::ScanningIssueKind;
use crate::metadata::class_like::ClassLikeMetadata;
use crate::metadata::class_like_constant::ClassLikeConstantMetadata;
use crate::metadata::flags::MetadataFlags;
use crate::scanner::Context;
use crate::scanner::attribute::scan_attribute_lists;
use crate::scanner::docblock::ConstantDocblockComment;
use crate::scanner::inference::infer;
use crate::scanner::ttype::get_type_metadata_from_hint;
use crate::scanner::ttype::get_type_metadata_from_type_string;
use crate::scanner::ttype::merge_type_preserving_nullability;
use crate::ttype::resolution::TypeResolutionContext;
use crate::visibility::Visibility;
use super::super::ttype::union::TUnion;
#[inline]
pub fn scan_class_like_constants<'arena>(
class_like_metadata: &mut ClassLikeMetadata,
constant: &'arena ClassLikeConstant<'arena>,
classname: Option<Atom>,
type_context: &TypeResolutionContext,
context: &mut Context<'_, 'arena>,
scope: &NamespaceScope,
) -> Vec<ClassLikeConstantMetadata> {
let attributes = scan_attribute_lists(&constant.attribute_lists, context);
let visibility =
constant.modifiers.get_first_visibility().and_then(|m| Visibility::try_from(m).ok()).unwrap_or_default();
let is_final = constant.modifiers.contains_final();
let type_declaration =
constant.hint.as_ref().map(|h| get_type_metadata_from_hint(h, Some(class_like_metadata.name), context));
let mut flags = if is_final { MetadataFlags::FINAL } else { MetadataFlags::empty() };
if context.file.file_type.is_host() {
flags |= MetadataFlags::USER_DEFINED;
} else if context.file.file_type.is_builtin() {
flags |= MetadataFlags::BUILTIN;
}
let docblock = match ConstantDocblockComment::create(context, constant) {
Ok(docblock) => docblock,
Err(parse_error) => {
class_like_metadata.issues.push(
Issue::error("Failed to parse constant docblock comment.")
.with_code(ScanningIssueKind::MalformedDocblockComment)
.with_annotation(Annotation::primary(parse_error.span()).with_message(parse_error.to_string()))
.with_note(parse_error.note())
.with_help(parse_error.help()),
);
None
}
};
constant
.items
.iter()
.map(|item| {
let mut meta = ClassLikeConstantMetadata::new(atom(item.name.value), item.span(), visibility, flags);
if let Some(type_declaration) = type_declaration.clone() {
meta.set_type_declaration(type_declaration);
}
meta.attributes.clone_from(&attributes);
meta.inferred_type = infer(context, scope, item.value).map(TUnion::get_single_owned);
if let Some(ref docblock) = docblock {
if docblock.is_deprecated {
meta.flags |= MetadataFlags::DEPRECATED;
}
if docblock.is_internal {
meta.flags |= MetadataFlags::INTERNAL;
}
if docblock.is_final {
meta.flags |= MetadataFlags::FINAL;
}
if let Some(type_string) = &docblock.type_string {
match get_type_metadata_from_type_string(type_string, classname, type_context, scope) {
Ok(type_metadata) => {
let real_type = meta.type_declaration.as_ref();
let type_metadata = merge_type_preserving_nullability(type_metadata, real_type);
meta.type_metadata = Some(type_metadata);
}
Err(typing_error) => class_like_metadata.issues.push(
Issue::error("Could not resolve the type for the @var tag.")
.with_code(ScanningIssueKind::InvalidVarTag)
.with_annotation(
Annotation::primary(typing_error.span()).with_message(typing_error.to_string()),
)
.with_note(typing_error.note())
.with_help(typing_error.help()),
),
}
}
}
meta
})
.collect()
}