1use php_ast::{
22 ClassConstDecl, ClassDecl, ClassMemberKind, EnumCase, EnumDecl, EnumMemberKind, FunctionDecl,
23 Ident, InterfaceDecl, MethodDecl, NamespaceBody, Param, PropertyDecl, Span, Stmt, StmtKind,
24 TraitDecl,
25};
26
27use crate::util::strip_variable_sigil;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum Container {
34 Class,
35 Interface,
36 Trait,
37 Enum,
38}
39
40pub enum Declaration<'a> {
44 Function {
45 decl: &'a FunctionDecl<'a, 'a>,
46 stmt_span: Span,
47 },
48 Class {
49 decl: &'a ClassDecl<'a, 'a>,
50 name: Ident<'a>,
52 stmt_span: Span,
53 },
54 Interface {
55 decl: &'a InterfaceDecl<'a, 'a>,
56 stmt_span: Span,
57 },
58 Trait {
59 decl: &'a TraitDecl<'a, 'a>,
60 stmt_span: Span,
61 },
62 Enum {
63 decl: &'a EnumDecl<'a, 'a>,
64 stmt_span: Span,
65 },
66 Method {
67 method: &'a MethodDecl<'a, 'a>,
68 container: Container,
69 member_span: Span,
70 },
71 ClassConst {
72 konst: &'a ClassConstDecl<'a, 'a>,
73 container: Container,
74 member_span: Span,
75 },
76 Property {
77 property: &'a PropertyDecl<'a, 'a>,
78 container: Container,
79 member_span: Span,
80 },
81 PromotedParam { param: &'a Param<'a, 'a> },
83 EnumCase {
84 case: &'a EnumCase<'a, 'a>,
85 enum_name: Ident<'a>,
86 member_span: Span,
87 },
88}
89
90impl<'a> Declaration<'a> {
91 pub fn name(&self) -> &'a str {
93 match self {
94 Declaration::Function { decl, .. } => decl.name.or_error(),
95 Declaration::Class { name, .. } => name.or_error(),
96 Declaration::Interface { decl, .. } => decl.name.or_error(),
97 Declaration::Trait { decl, .. } => decl.name.or_error(),
98 Declaration::Enum { decl, .. } => decl.name.or_error(),
99 Declaration::Method { method, .. } => method.name.or_error(),
100 Declaration::ClassConst { konst, .. } => konst.name.or_error(),
101 Declaration::Property { property, .. } => property.name.or_error(),
102 Declaration::PromotedParam { param } => param.name.or_error(),
103 Declaration::EnumCase { case, .. } => case.name.or_error(),
104 }
105 }
106
107 pub fn span(&self) -> Span {
108 match self {
109 Declaration::Function { stmt_span, .. } => *stmt_span,
110 Declaration::Class { stmt_span, .. } => *stmt_span,
111 Declaration::Interface { stmt_span, .. } => *stmt_span,
112 Declaration::Trait { stmt_span, .. } => *stmt_span,
113 Declaration::Enum { stmt_span, .. } => *stmt_span,
114 Declaration::Method { member_span, .. } => *member_span,
115 Declaration::ClassConst { member_span, .. } => *member_span,
116 Declaration::Property { member_span, .. } => *member_span,
117 Declaration::PromotedParam { param } => param.span,
118 Declaration::EnumCase { member_span, .. } => *member_span,
119 }
120 }
121}
122
123pub fn resolve_declaration<'a>(
129 stmts: &'a [Stmt<'a, 'a>],
130 word: &str,
131 accept: &dyn Fn(&Declaration<'a>) -> bool,
132) -> Option<Declaration<'a>> {
133 let bare = strip_variable_sigil(word);
134 for stmt in stmts {
135 match &stmt.kind {
136 StmtKind::Function(f) if f.name == word => {
137 let d = Declaration::Function {
138 decl: f,
139 stmt_span: stmt.span,
140 };
141 if accept(&d) {
142 return Some(d);
143 }
144 }
145 StmtKind::Class(c) => {
146 if let Some(name) = c.name
149 && name.or_error() == word
150 {
151 let d = Declaration::Class {
152 decl: c,
153 name,
154 stmt_span: stmt.span,
155 };
156 if accept(&d) {
157 return Some(d);
158 }
159 }
160 if let Some(d) =
161 resolve_member(c.body.members.iter(), word, bare, Container::Class, accept)
162 {
163 return Some(d);
164 }
165 }
166 StmtKind::Interface(i) => {
167 if i.name == word {
168 let d = Declaration::Interface {
169 decl: i,
170 stmt_span: stmt.span,
171 };
172 if accept(&d) {
173 return Some(d);
174 }
175 }
176 if let Some(d) = resolve_member(
177 i.body.members.iter(),
178 word,
179 bare,
180 Container::Interface,
181 accept,
182 ) {
183 return Some(d);
184 }
185 }
186 StmtKind::Trait(t) => {
187 if t.name == word {
188 let d = Declaration::Trait {
189 decl: t,
190 stmt_span: stmt.span,
191 };
192 if accept(&d) {
193 return Some(d);
194 }
195 }
196 if let Some(d) =
197 resolve_member(t.body.members.iter(), word, bare, Container::Trait, accept)
198 {
199 return Some(d);
200 }
201 }
202 StmtKind::Enum(e) => {
203 if e.name == word {
204 let d = Declaration::Enum {
205 decl: e,
206 stmt_span: stmt.span,
207 };
208 if accept(&d) {
209 return Some(d);
210 }
211 }
212 for member in e.body.members.iter() {
213 match &member.kind {
214 EnumMemberKind::Case(c) if c.name == word => {
215 let d = Declaration::EnumCase {
216 case: c,
217 enum_name: e.name,
218 member_span: member.span,
219 };
220 if accept(&d) {
221 return Some(d);
222 }
223 }
224 EnumMemberKind::Method(m) if m.name == word => {
225 let d = Declaration::Method {
226 method: m,
227 container: Container::Enum,
228 member_span: member.span,
229 };
230 if accept(&d) {
231 return Some(d);
232 }
233 }
234 EnumMemberKind::ClassConst(cc) if cc.name == word => {
235 let d = Declaration::ClassConst {
236 konst: cc,
237 container: Container::Enum,
238 member_span: member.span,
239 };
240 if accept(&d) {
241 return Some(d);
242 }
243 }
244 _ => {}
245 }
246 }
247 }
248 StmtKind::Namespace(ns) => {
249 if let NamespaceBody::Braced(inner) = &ns.body
250 && let Some(d) = resolve_declaration(&inner.stmts, word, accept)
251 {
252 return Some(d);
253 }
254 }
255 _ => {}
256 }
257 }
258 None
259}
260
261fn resolve_member<'a>(
264 members: impl Iterator<Item = &'a php_ast::ClassMember<'a, 'a>>,
265 word: &str,
266 bare: &str,
267 container: Container,
268 accept: &dyn Fn(&Declaration<'a>) -> bool,
269) -> Option<Declaration<'a>> {
270 for member in members {
271 match &member.kind {
272 ClassMemberKind::Method(m) => {
273 if m.name == word {
274 let d = Declaration::Method {
275 method: m,
276 container,
277 member_span: member.span,
278 };
279 if accept(&d) {
280 return Some(d);
281 }
282 }
283 if container == Container::Class && m.name == "__construct" {
285 for p in m.params.iter() {
286 if p.visibility.is_some() && p.name == bare {
287 let d = Declaration::PromotedParam { param: p };
288 if accept(&d) {
289 return Some(d);
290 }
291 }
292 }
293 }
294 }
295 ClassMemberKind::ClassConst(cc) if cc.name == word => {
296 let d = Declaration::ClassConst {
297 konst: cc,
298 container,
299 member_span: member.span,
300 };
301 if accept(&d) {
302 return Some(d);
303 }
304 }
305 ClassMemberKind::Property(p) if p.name == bare => {
306 let d = Declaration::Property {
307 property: p,
308 container,
309 member_span: member.span,
310 };
311 if accept(&d) {
312 return Some(d);
313 }
314 }
315 _ => {}
316 }
317 }
318 None
319}