use super::DefinitionCollector;
use crate::parser::{name_to_string, type_from_hint};
use mir_codebase::storage::{ConstantStorage, PropertyStorage, TemplateParam, TraitStorage};
use mir_types::Union;
use php_ast::ast::{ClassMemberKind, TraitDecl};
use std::ops::ControlFlow;
use std::sync::Arc;
impl<'a> DefinitionCollector<'a> {
pub(super) fn collect_trait<'arena, 'src>(
&mut self,
decl: &TraitDecl<'arena, 'src>,
stmt_span: php_ast::Span,
) -> ControlFlow<()> {
let fqcn = self.resolve_name(decl.name);
let trait_doc = decl
.doc_comment
.as_ref()
.map(|c| crate::parser::DocblockParser::parse(c.text))
.or_else(|| {
crate::parser::find_preceding_docblock(self.source, stmt_span.start)
.map(|t| crate::parser::DocblockParser::parse(&t))
})
.unwrap_or_default();
let trait_doc_span = decl
.doc_comment
.as_ref()
.map(|c| c.span.start)
.unwrap_or(stmt_span.start);
self.emit_docblock_issues(&trait_doc, trait_doc_span);
if !self.version_allows(&trait_doc) {
return ControlFlow::Continue(());
}
let trait_template_params: Vec<TemplateParam> = trait_doc
.templates
.iter()
.map(|(name, bound, variance)| TemplateParam {
name: name.as_str().into(),
bound: bound.clone(),
defining_entity: fqcn.as_str().into(),
variance: *variance,
})
.collect();
let mut own_methods = indexmap::IndexMap::new();
let mut own_properties = indexmap::IndexMap::new();
let mut own_constants = indexmap::IndexMap::new();
let mut trait_uses: Vec<Arc<str>> = vec![];
for member in decl.members.iter() {
match &member.kind {
ClassMemberKind::Method(m) => {
if m.name == "__construct" {
for p in m.params.iter() {
if p.visibility.is_some() {
let ty = self.resolve_union_opt(
p.type_hint.as_ref().map(|h| type_from_hint(h, Some(&fqcn))),
);
let prop = PropertyStorage {
name: p.name.into(),
ty,
inferred_ty: None,
visibility: Self::convert_visibility(p.visibility),
is_static: false,
is_readonly: p.is_readonly,
default: p.default.as_ref().map(|_| Union::mixed()),
location: Some(
self.location(member.span.start, member.span.end),
),
};
own_properties.insert(p.name.into(), prop);
}
}
}
if let Some(method) =
self.build_method_storage(m, &fqcn, Some(&member.span), None)
{
own_methods.insert(
Arc::from(method.name.to_lowercase().as_str()),
Arc::new(method),
);
}
}
ClassMemberKind::Property(p) => {
let prop_doc = p
.doc_comment
.as_ref()
.map(|c| crate::parser::DocblockParser::parse(c.text))
.or_else(|| {
crate::parser::find_preceding_docblock(self.source, member.span.start)
.map(|t| crate::parser::DocblockParser::parse(&t))
})
.unwrap_or_default();
let prop_doc_span = p
.doc_comment
.as_ref()
.map(|c| c.span.start)
.unwrap_or(member.span.start);
self.emit_docblock_issues(&prop_doc, prop_doc_span);
if !self.version_allows(&prop_doc) {
continue;
}
own_properties.insert(
Arc::from(p.name),
PropertyStorage {
name: p.name.into(),
ty: self.resolve_union_opt(
p.type_hint.as_ref().map(|h| type_from_hint(h, Some(&fqcn))),
),
inferred_ty: None,
visibility: Self::convert_visibility(p.visibility),
is_static: p.is_static,
is_readonly: p.is_readonly,
default: None,
location: Some(self.location(member.span.start, member.span.end)),
},
);
}
ClassMemberKind::ClassConst(c) => {
let const_doc = c
.doc_comment
.as_ref()
.map(|c| crate::parser::DocblockParser::parse(c.text))
.or_else(|| {
crate::parser::find_preceding_docblock(self.source, member.span.start)
.map(|t| crate::parser::DocblockParser::parse(&t))
})
.unwrap_or_default();
let const_doc_span = c
.doc_comment
.as_ref()
.map(|c| c.span.start)
.unwrap_or(member.span.start);
self.emit_docblock_issues(&const_doc, const_doc_span);
if !self.version_allows(&const_doc) {
continue;
}
own_constants.insert(
Arc::from(c.name),
ConstantStorage {
name: c.name.into(),
ty: Union::mixed(),
visibility: None,
is_final: c.is_final,
location: Some(self.location(member.span.start, member.span.end)),
},
);
}
ClassMemberKind::TraitUse(tu) => {
for t in tu.traits.iter() {
trait_uses.push(self.resolve_name(&name_to_string(t)).into());
}
}
}
}
let require_extends: Vec<Arc<str>> = trait_doc
.require_extends
.iter()
.map(|s| self.resolve_type_name(&Arc::from(s.as_str()), true))
.collect();
let require_implements: Vec<Arc<str>> = trait_doc
.require_implements
.iter()
.map(|s| self.resolve_type_name(&Arc::from(s.as_str()), true))
.collect();
self.slice.traits.push(TraitStorage {
fqcn: fqcn.into(),
short_name: decl.name.into(),
own_methods,
own_properties,
own_constants,
template_params: trait_template_params,
traits: trait_uses,
location: Some(self.location(stmt_span.start, stmt_span.end)),
require_extends,
require_implements,
});
ControlFlow::Continue(())
}
}