use super::DefinitionCollector;
use crate::parser::{name_to_string_owned, type_from_hint_owned};
use mir_codebase::storage::{ConstantDef, PropertyDef, TemplateParam, TraitDef};
use mir_types::Type;
use php_ast::owned::{ClassMemberKind, TraitDecl};
use std::ops::ControlFlow;
use std::sync::Arc;
impl<'a> DefinitionCollector<'a> {
pub(super) fn collect_trait(
&mut self,
decl: &TraitDecl,
stmt_span: php_ast::Span,
) -> ControlFlow<()> {
let trait_name = decl.name.as_deref().unwrap_or_default().to_string();
let fqcn = self.resolve_name(&trait_name);
let trait_doc = decl
.doc_comment
.as_ref()
.map(|c| crate::parser::DocblockParser::parse(&c.text))
.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_names: std::collections::HashSet<String> = trait_doc
.templates
.iter()
.map(|(n, _, _)| n.to_string())
.collect();
let trait_template_params: Vec<TemplateParam> = trait_doc
.templates
.iter()
.map(|(name, bound, variance)| TemplateParam {
name: name.as_str().into(),
bound: bound.clone().map(|b| {
self.resolve_union_doc_with_templates(
b,
&trait_template_names,
fqcn.as_str(),
&[],
)
}),
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.body.members.iter() {
match &member.kind {
ClassMemberKind::Method(m) => {
if m.name.as_deref() == Some("__construct") {
for p in m.params.iter() {
if p.visibility.is_some() {
let param_name = p.name.as_deref().unwrap_or_default();
let ty = self.resolve_union_opt(
p.type_hint
.as_ref()
.map(|h| type_from_hint_owned(h, Some(&fqcn))),
);
let prop = PropertyDef {
name: Arc::from(param_name),
ty: mir_codebase::storage::wrap_property_type(ty),
inferred_ty: None,
visibility: Self::convert_visibility(p.visibility),
is_static: false,
is_readonly: p.is_readonly,
default: mir_codebase::storage::wrap_property_type(
p.default.as_ref().map(|_| Type::mixed()),
),
location: Some(
self.location(member.span.start, member.span.end),
),
deprecated: None,
};
own_properties.insert(Arc::from(param_name), prop);
}
}
}
if let Some(method) = self.build_method_storage(
m,
&fqcn,
Some(&member.span),
None,
&trait_template_params,
) {
own_methods.insert(
Arc::from(method.name.to_lowercase().as_str()),
Arc::new(method),
);
}
}
ClassMemberKind::Property(p) => {
let prop_doc = self.parse_docblock_from_node(p.doc_comment.as_ref());
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;
}
let prop_name = p.name.as_deref().unwrap_or_default();
own_properties.insert(
Arc::from(prop_name),
PropertyDef {
name: Arc::from(prop_name),
ty: mir_codebase::storage::wrap_property_type(
self.resolve_union_opt(
p.type_hint
.as_ref()
.map(|h| type_from_hint_owned(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)),
deprecated: prop_doc.deprecated.as_deref().map(Arc::from),
},
);
}
ClassMemberKind::ClassConst(c) => {
let const_doc = self.parse_docblock_from_node(c.doc_comment.as_ref());
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;
}
let const_name = c.name.as_deref().unwrap_or_default();
own_constants.insert(
Arc::from(const_name),
ConstantDef {
name: Arc::from(const_name),
ty: Type::mixed(),
visibility: None,
is_final: c.is_final,
location: Some(self.location(member.span.start, member.span.end)),
deprecated: None,
},
);
}
ClassMemberKind::TraitUse(tu) => {
for t in tu.traits.iter() {
trait_uses.push(self.resolve_name(&name_to_string_owned(t)).into());
}
}
}
}
let type_aliases = self.build_type_aliases(&trait_doc);
self.add_docblock_members(
&trait_doc,
&type_aliases,
&fqcn,
&mut own_methods,
&mut own_properties,
Some(self.location(stmt_span.start, stmt_span.end)),
);
let require_extends: Vec<Arc<str>> = trait_doc
.require_extends
.iter()
.map(|s| self.resolve_type_name(s.as_str(), true).into())
.collect();
let require_implements: Vec<Arc<str>> = trait_doc
.require_implements
.iter()
.map(|s| self.resolve_type_name(s.as_str(), true).into())
.collect();
self.slice.traits.push(std::sync::Arc::new(TraitDef {
fqcn: fqcn.into(),
short_name: Arc::from(trait_name.as_str()),
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,
deprecated: trait_doc.deprecated.as_deref().map(Arc::from),
}));
ControlFlow::Continue(())
}
}