1 //! Type inference
2 //! THIS MODULE IS ENTIRELY TOO UGLY SO REALLY NEADS REFACTORING(kngwyu)
3 use crate::ast;
4 use crate::ast_types::{Pat, Ty};
5 use crate::core;
6 use crate::core::{
7 BytePos, ByteRange, Match, MatchType, Namespace, Scope, SearchType, Session, SessionExt, Src,
8 };
9 use crate::matchers;
10 use crate::nameres;
11 use crate::primitive::PrimKind;
12 use crate::scopes;
13 use crate::util::{self, txt_matches};
14 use rustc_ast::ast::BinOpKind;
15 use std::path::Path;
16
17 // Removes the body of the statement (anything in the braces {...}), leaving just
18 // the header
generate_skeleton_for_parsing(src: &str) -> Option<String>19 pub fn generate_skeleton_for_parsing(src: &str) -> Option<String> {
20 src.find('{').map(|n| src[..=n].to_owned() + "}")
21 }
22
23 /// Get the trait name implementing which overrides the operator `op`
24 /// For comparison operators, it is `bool`
get_operator_trait(op: BinOpKind) -> &'static str25 pub(crate) fn get_operator_trait(op: BinOpKind) -> &'static str {
26 match op {
27 BinOpKind::Add => "Add",
28 BinOpKind::Sub => "Sub",
29 BinOpKind::Mul => "Mul",
30 BinOpKind::Div => "Div",
31 BinOpKind::Rem => "Rem",
32 BinOpKind::And => "And",
33 BinOpKind::Or => "Or",
34 BinOpKind::BitXor => "BitXor",
35 BinOpKind::BitAnd => "BitAnd",
36 BinOpKind::BitOr => "BitOr",
37 BinOpKind::Shl => "Shl",
38 BinOpKind::Shr => "Shr",
39 _ => "bool",
40 }
41 }
42
43 // TODO(kngwyu): use libsyntax parser
first_param_is_self(blob: &str) -> bool44 pub fn first_param_is_self(blob: &str) -> bool {
45 // Restricted visibility introduces the possibility of `pub(in ...)` at the start
46 // of a method declaration. To counteract this, we restrict the search to only
47 // look at text _after_ the visibility declaration.
48 //
49 // Having found the end of the visibility declaration, we now start the search
50 // for method parameters.
51 let blob = util::trim_visibility(blob);
52
53 // skip generic arg
54 // consider 'pub fn map<U, F: FnOnce(T) -> U>(self, f: F)'
55 // we have to match the '>'
56 match blob.find('(') {
57 None => false,
58 Some(probable_param_start) => {
59 let skip_generic = match blob.find('<') {
60 None => 0,
61 Some(generic_start) if generic_start < probable_param_start => {
62 let mut level = 0;
63 let mut prev = ' ';
64 let mut skip_generic = 0;
65 for (i, c) in blob[generic_start..].char_indices() {
66 match c {
67 '<' => level += 1,
68 '>' if prev == '-' => (),
69 '>' => level -= 1,
70 _ => (),
71 }
72 prev = c;
73 if level == 0 {
74 skip_generic = i;
75 break;
76 }
77 }
78 skip_generic
79 }
80 Some(..) => 0,
81 };
82 if let Some(start) = blob[skip_generic..].find('(') {
83 let start = BytePos::from(skip_generic + start).increment();
84 let end = scopes::find_closing_paren(blob, start);
85 let is_self = txt_matches(SearchType::ExactMatch, "self", &blob[start.0..end.0]);
86 trace!(
87 "searching fn args for self: |{}| {}",
88 &blob[start.0..end.0],
89 is_self
90 );
91 return is_self;
92 }
93 false
94 }
95 }
96 }
97
98 #[test]
generates_skeleton_for_mod()99 fn generates_skeleton_for_mod() {
100 let src = "mod foo { blah }";
101 let out = generate_skeleton_for_parsing(src).unwrap();
102 assert_eq!("mod foo {}", out);
103 }
104
get_type_of_self_arg(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty>105 fn get_type_of_self_arg(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty> {
106 debug!("get_type_of_self_arg {:?}", m);
107 get_type_of_self(m.point, &m.filepath, m.local, msrc, session)
108 }
109
110 // TODO(kngwyu): parse correctly
get_type_of_self( point: BytePos, filepath: &Path, local: bool, msrc: Src<'_>, session: &Session<'_>, ) -> Option<Ty>111 pub fn get_type_of_self(
112 point: BytePos,
113 filepath: &Path,
114 local: bool,
115 msrc: Src<'_>,
116 session: &Session<'_>,
117 ) -> Option<Ty> {
118 let start = scopes::find_impl_start(msrc, point, BytePos::ZERO)?;
119 let decl = generate_skeleton_for_parsing(&msrc.shift_start(start))?;
120 debug!("get_type_of_self_arg impl skeleton |{}|", decl);
121 if decl.starts_with("impl") {
122 // we have to do 2 operations around generics here
123 // 1. Checks if self's type is T
124 // 2. Checks if self's type contains T
125 let scope_start = start + decl.len().into();
126 let implres = ast::parse_impl(decl, filepath, start, local, scope_start)?;
127 if let Some((_, param)) = implres.generics().search_param_by_path(implres.self_path()) {
128 if let Some(resolved) = param.resolved() {
129 return Some(resolved.to_owned());
130 }
131 let mut m = param.to_owned().into_match();
132 m.local = local;
133 return Some(Ty::Match(m));
134 }
135 debug!("get_type_of_self_arg implres |{:?}|", implres);
136 nameres::resolve_path(
137 implres.self_path(),
138 filepath,
139 start,
140 SearchType::ExactMatch,
141 Namespace::Type,
142 session,
143 &matchers::ImportInfo::default(),
144 )
145 .into_iter()
146 .nth(0)
147 .map(|mut m| {
148 match &mut m.mtype {
149 MatchType::Enum(gen) | MatchType::Struct(gen) => {
150 for (i, param) in implres.generics.0.into_iter().enumerate() {
151 gen.add_bound(i, param.bounds);
152 }
153 }
154 _ => {}
155 }
156 Ty::Match(m)
157 })
158 } else {
159 // // must be a trait
160 ast::parse_trait(decl).name.and_then(|name| {
161 Some(Ty::Match(Match {
162 matchstr: name,
163 filepath: filepath.into(),
164 point: start,
165 coords: None,
166 local: local,
167 mtype: core::MatchType::Trait,
168 contextstr: matchers::first_line(&msrc[start.0..]),
169 docs: String::new(),
170 }))
171 })
172 }
173 }
174
get_type_of_fnarg(m: Match, session: &Session<'_>) -> Option<Ty>175 fn get_type_of_fnarg(m: Match, session: &Session<'_>) -> Option<Ty> {
176 let Match {
177 matchstr,
178 filepath,
179 point,
180 mtype,
181 ..
182 } = m;
183 let (pat, ty) = *match mtype {
184 MatchType::FnArg(a) => a,
185 _ => return None,
186 };
187 resolve_lvalue_ty(pat, ty, &matchstr, &filepath, point, session)
188 }
189
get_type_of_let_expr(m: Match, session: &Session<'_>) -> Option<Ty>190 fn get_type_of_let_expr(m: Match, session: &Session<'_>) -> Option<Ty> {
191 let Match {
192 mtype,
193 contextstr,
194 filepath,
195 point,
196 ..
197 } = m;
198 let let_start = match mtype {
199 MatchType::Let(s) => s,
200 _ => return None,
201 };
202 debug!("get_type_of_let_expr calling parse_let |{}|", contextstr);
203 let pos = point - let_start;
204 let scope = Scope {
205 filepath,
206 point: let_start,
207 };
208 ast::get_let_type(contextstr, pos, scope, session)
209 }
210
211 /// Decide l_value's type given r_value and ident query
resolve_lvalue_ty<'a>( l_value: Pat, r_value: Option<Ty>, query: &str, fpath: &Path, pos: BytePos, session: &Session<'_>, ) -> Option<Ty>212 pub(crate) fn resolve_lvalue_ty<'a>(
213 l_value: Pat,
214 r_value: Option<Ty>,
215 query: &str,
216 fpath: &Path,
217 pos: BytePos,
218 session: &Session<'_>,
219 ) -> Option<Ty> {
220 match l_value {
221 Pat::Ident(_bi, name) => {
222 if name != query {
223 return None;
224 }
225 r_value
226 }
227 Pat::Tuple(pats) => {
228 if let Ty::Tuple(ty) = r_value? {
229 for (p, t) in pats.into_iter().zip(ty) {
230 let ret = try_continue!(resolve_lvalue_ty(p, t, query, fpath, pos, session,));
231 return Some(ret);
232 }
233 }
234 None
235 }
236 Pat::Ref(pat, _) => {
237 if let Some(ty) = r_value {
238 if let Ty::RefPtr(ty, _) = ty {
239 resolve_lvalue_ty(*pat, Some(*ty), query, fpath, pos, session)
240 } else {
241 resolve_lvalue_ty(*pat, Some(ty), query, fpath, pos, session)
242 }
243 } else {
244 resolve_lvalue_ty(*pat, None, query, fpath, pos, session)
245 }
246 }
247 Pat::TupleStruct(path, pats) => {
248 let ma = ast::find_type_match(&path, fpath, pos, session)?;
249 match &ma.mtype {
250 MatchType::Struct(_generics) => {
251 for (pat, (_, _, t)) in
252 pats.into_iter().zip(get_tuplestruct_fields(&ma, session))
253 {
254 let ret =
255 try_continue!(resolve_lvalue_ty(pat, t, query, fpath, pos, session));
256 return Some(ret);
257 }
258 None
259 }
260 MatchType::EnumVariant(enum_) => {
261 let generics = if let Some(Ty::Match(match_)) = r_value.map(Ty::dereference) {
262 match_.into_generics()
263 } else {
264 enum_.to_owned().and_then(|ma| ma.into_generics())
265 };
266 for (pat, (_, _, mut t)) in
267 pats.into_iter().zip(get_tuplestruct_fields(&ma, session))
268 {
269 debug!(
270 "Hi! I'm in enum and l: {:?}\n r: {:?}\n gen: {:?}",
271 pat, t, generics
272 );
273 if let Some(ref gen) = generics {
274 t = t.map(|ty| ty.replace_by_resolved_generics(&gen));
275 }
276 let ret =
277 try_continue!(resolve_lvalue_ty(pat, t, query, fpath, pos, session));
278 return Some(ret);
279 }
280 None
281 }
282 _ => None,
283 }
284 }
285 // Let's implement after #946 solved
286 Pat::Struct(path, _) => {
287 let item = ast::find_type_match(&path, fpath, pos, session)?;
288 if !item.mtype.is_struct() {
289 return None;
290 }
291 None
292 }
293 _ => None,
294 }
295 }
296
get_type_of_for_arg(m: &Match, session: &Session<'_>) -> Option<Ty>297 fn get_type_of_for_arg(m: &Match, session: &Session<'_>) -> Option<Ty> {
298 let for_start = match &m.mtype {
299 MatchType::For(pos) => *pos,
300 _ => {
301 warn!("[get_type_of_for_expr] invalid match type: {:?}", m.mtype);
302 return None;
303 }
304 };
305 // HACK: use outer scope when getting in ~ expr's type
306 let scope = Scope::new(m.filepath.clone(), for_start);
307 let ast::ForStmtVisitor {
308 for_pat, in_expr, ..
309 } = ast::parse_for_stmt(m.contextstr.clone(), scope, session);
310 debug!(
311 "[get_type_of_for_expr] match: {:?}, for: {:?}, in: {:?},",
312 m, for_pat, in_expr
313 );
314 fn get_item(ty: Ty, session: &Session<'_>) -> Option<Ty> {
315 match ty {
316 Ty::Match(ma) => nameres::get_iter_item(&ma, session),
317 Ty::PathSearch(paths) => {
318 nameres::get_iter_item(&paths.resolve_as_match(session)?, session)
319 }
320 Ty::RefPtr(ty, _) => get_item(*ty, session),
321 _ => None,
322 }
323 }
324 resolve_lvalue_ty(
325 for_pat?,
326 in_expr.and_then(|ty| get_item(ty, session)),
327 &m.matchstr,
328 &m.filepath,
329 m.point,
330 session,
331 )
332 }
333
get_type_of_if_let(m: &Match, session: &Session<'_>, start: BytePos) -> Option<Ty>334 fn get_type_of_if_let(m: &Match, session: &Session<'_>, start: BytePos) -> Option<Ty> {
335 // HACK: use outer scope when getting r-value's type
336 let scope = Scope::new(m.filepath.clone(), start);
337 let ast::IfLetVisitor {
338 let_pat, rh_expr, ..
339 } = ast::parse_if_let(m.contextstr.clone(), scope, session);
340 debug!(
341 "[get_type_of_if_let] match: {:?}\n let: {:?}\n rh: {:?},",
342 m, let_pat, rh_expr,
343 );
344 resolve_lvalue_ty(
345 let_pat?,
346 rh_expr,
347 &m.matchstr,
348 &m.filepath,
349 m.point,
350 session,
351 )
352 }
353
get_struct_field_type( fieldname: &str, structmatch: &Match, session: &Session<'_>, ) -> Option<Ty>354 pub fn get_struct_field_type(
355 fieldname: &str,
356 structmatch: &Match,
357 session: &Session<'_>,
358 ) -> Option<Ty> {
359 // temporary fix for https://github.com/rust-lang-nursery/rls/issues/783
360 if !structmatch.mtype.is_struct() {
361 warn!(
362 "get_struct_filed_type is called for {:?}",
363 structmatch.mtype
364 );
365 return None;
366 }
367 debug!("[get_struct_filed_type]{}, {:?}", fieldname, structmatch);
368
369 let src = session.load_source_file(&structmatch.filepath);
370
371 let opoint = scopes::expect_stmt_start(src.as_src(), structmatch.point);
372 // HACK: if scopes::end_of_next_scope returns empty struct, it's maybe tuple struct
373 let structsrc = if let Some(end) = scopes::end_of_next_scope(&src[opoint.0..]) {
374 src[opoint.0..=(opoint + end).0].to_owned()
375 } else {
376 (*get_first_stmt(src.as_src().shift_start(opoint))).to_owned()
377 };
378 let fields = ast::parse_struct_fields(structsrc.to_owned(), Scope::from_match(structmatch));
379 for (field, _, ty) in fields {
380 if fieldname != field {
381 continue;
382 }
383 return ty;
384 }
385 None
386 }
387
get_tuplestruct_fields( structmatch: &Match, session: &Session<'_>, ) -> Vec<(String, ByteRange, Option<Ty>)>388 pub(crate) fn get_tuplestruct_fields(
389 structmatch: &Match,
390 session: &Session<'_>,
391 ) -> Vec<(String, ByteRange, Option<Ty>)> {
392 let src = session.load_source_file(&structmatch.filepath);
393 let structsrc = if let core::MatchType::EnumVariant(_) = structmatch.mtype {
394 // decorate the enum variant src to make it look like a tuple struct
395 let to = src[structmatch.point.0..]
396 .find('(')
397 .map(|n| {
398 scopes::find_closing_paren(&src, structmatch.point + BytePos::from(n).increment())
399 })
400 .expect("Tuple enum variant should have `(` in definition");
401 "struct ".to_owned() + &src[structmatch.point.0..to.increment().0] + ";"
402 } else {
403 assert!(structmatch.mtype.is_struct());
404 let opoint = scopes::expect_stmt_start(src.as_src(), structmatch.point);
405 (*get_first_stmt(src.as_src().shift_start(opoint))).to_owned()
406 };
407
408 debug!("[tuplestruct_fields] structsrc=|{}|", structsrc);
409
410 ast::parse_struct_fields(structsrc, Scope::from_match(structmatch))
411 }
412
get_tuplestruct_field_type( fieldnum: usize, structmatch: &Match, session: &Session<'_>, ) -> Option<Ty>413 pub fn get_tuplestruct_field_type(
414 fieldnum: usize,
415 structmatch: &Match,
416 session: &Session<'_>,
417 ) -> Option<Ty> {
418 let fields = get_tuplestruct_fields(structmatch, session);
419
420 for (i, (_, _, ty)) in fields.into_iter().enumerate() {
421 if i == fieldnum {
422 return ty;
423 }
424 }
425 None
426 }
427
get_first_stmt(src: Src<'_>) -> Src<'_>428 pub fn get_first_stmt(src: Src<'_>) -> Src<'_> {
429 match src.iter_stmts().next() {
430 Some(range) => src.shift_range(range),
431 None => src,
432 }
433 }
434
get_type_of_match(m: Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty>435 pub fn get_type_of_match(m: Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty> {
436 debug!("get_type_of match {:?} ", m);
437
438 match m.mtype {
439 core::MatchType::Let(_) => get_type_of_let_expr(m, session),
440 core::MatchType::IfLet(start) | core::MatchType::WhileLet(start) => {
441 get_type_of_if_let(&m, session, start)
442 }
443 core::MatchType::For(_) => get_type_of_for_arg(&m, session),
444 core::MatchType::FnArg(_) => get_type_of_fnarg(m, session),
445 core::MatchType::MatchArm => get_type_from_match_arm(&m, msrc, session),
446 core::MatchType::Struct(_)
447 | core::MatchType::Union(_)
448 | core::MatchType::Enum(_)
449 | core::MatchType::Function
450 | core::MatchType::Method(_)
451 | core::MatchType::Module => Some(Ty::Match(m)),
452 core::MatchType::Const | core::MatchType::Static => get_type_of_static(m),
453 core::MatchType::EnumVariant(Some(boxed_enum)) => {
454 if boxed_enum.mtype.is_enum() {
455 Some(Ty::Match(*boxed_enum))
456 } else {
457 debug!("EnumVariant has not-enum type: {:?}", boxed_enum.mtype);
458 None
459 }
460 }
461 _ => {
462 debug!("!!! WARNING !!! Can't get type of {:?}", m.mtype);
463 None
464 }
465 }
466 }
467
get_type_from_match_arm(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty>468 pub fn get_type_from_match_arm(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty> {
469 // We construct a faux match stmt and then parse it. This is because the
470 // match stmt may be incomplete (half written) in the real code
471
472 // skip to end of match arm pattern so we can search backwards
473 let arm = BytePos(msrc[m.point.0..].find("=>")?) + m.point;
474 let scopestart = scopes::scope_start(msrc, arm);
475
476 let stmtstart = scopes::find_stmt_start(msrc, scopestart.decrement())?;
477 debug!("PHIL preblock is {:?} {:?}", stmtstart, scopestart);
478 let preblock = &msrc[stmtstart.0..scopestart.0];
479 let matchstart = stmtstart + preblock.rfind("match ")?.into();
480
481 let lhs_start = scopes::get_start_of_pattern(&msrc, arm);
482 let lhs = &msrc[lhs_start.0..arm.0];
483 // construct faux match statement and recreate point
484 let mut fauxmatchstmt = msrc[matchstart.0..scopestart.0].to_owned();
485 let faux_prefix_size = BytePos::from(fauxmatchstmt.len());
486 fauxmatchstmt = fauxmatchstmt + lhs + " => () };";
487 let faux_point = faux_prefix_size + (m.point - lhs_start);
488
489 debug!(
490 "fauxmatchstmt for parsing is pt:{:?} src:|{}|",
491 faux_point, fauxmatchstmt
492 );
493
494 ast::get_match_arm_type(
495 fauxmatchstmt,
496 faux_point,
497 // scope is used to locate expression, so send
498 // it the start of the match expr
499 Scope {
500 filepath: m.filepath.clone(),
501 point: matchstart,
502 },
503 session,
504 )
505 }
506
get_function_declaration(fnmatch: &Match, session: &Session<'_>) -> String507 pub fn get_function_declaration(fnmatch: &Match, session: &Session<'_>) -> String {
508 let src = session.load_source_file(&fnmatch.filepath);
509 let start = scopes::expect_stmt_start(src.as_src(), fnmatch.point);
510 let def_end: &[_] = &['{', ';'];
511 let end = src[start.0..]
512 .find(def_end)
513 .expect("Definition should have an end (`{` or `;`)");
514 src[start.0..start.0 + end].to_owned()
515 }
516
get_return_type_of_function( fnmatch: &Match, contextm: &Match, session: &Session<'_>, ) -> Option<Ty>517 pub fn get_return_type_of_function(
518 fnmatch: &Match,
519 contextm: &Match,
520 session: &Session<'_>,
521 ) -> Option<Ty> {
522 let src = session.load_source_file(&fnmatch.filepath);
523 let point = scopes::expect_stmt_start(src.as_src(), fnmatch.point);
524 let block_start = src[point.0..].find('{')?;
525 let decl = "impl b{".to_string() + &src[point.0..point.0 + block_start + 1] + "}}";
526 debug!("get_return_type_of_function: passing in |{}|", decl);
527 let mut scope = Scope::from_match(fnmatch);
528 // TODO(kngwyu): if point <= 5 scope is incorrect
529 scope.point = point.checked_sub("impl b{".len()).unwrap_or(BytePos::ZERO);
530 let (ty, is_async) = ast::parse_fn_output(decl, scope);
531 let resolve_ty = |ty| {
532 if let Some(Ty::PathSearch(ref paths)) = ty {
533 let path = &paths.path;
534 if let Some(ref path_seg) = path.segments.get(0) {
535 if "Self" == path_seg.name {
536 return get_type_of_self_arg(fnmatch, src.as_src(), session);
537 }
538 if path.segments.len() == 1 && path_seg.generics.is_empty() {
539 for type_param in fnmatch.generics() {
540 if type_param.name() == &path_seg.name {
541 return Some(Ty::Match(contextm.clone()));
542 }
543 }
544 }
545 }
546 }
547 ty
548 };
549 resolve_ty(ty).map(|ty| {
550 if is_async {
551 Ty::Future(Box::new(ty), Scope::from_match(fnmatch))
552 } else {
553 ty
554 }
555 })
556 }
557
get_type_of_indexed_value(body: Ty, session: &Session<'_>) -> Option<Ty>558 pub(crate) fn get_type_of_indexed_value(body: Ty, session: &Session<'_>) -> Option<Ty> {
559 match body.dereference() {
560 Ty::Match(m) => nameres::get_index_output(&m, session),
561 Ty::PathSearch(p) => p
562 .resolve_as_match(session)
563 .and_then(|m| nameres::get_index_output(&m, session)),
564 Ty::Array(ty, _) | Ty::Slice(ty) => Some(*ty),
565 _ => None,
566 }
567 }
568
get_type_of_typedef(m: &Match, session: &Session<'_>) -> Option<Match>569 pub(crate) fn get_type_of_typedef(m: &Match, session: &Session<'_>) -> Option<Match> {
570 debug!("get_type_of_typedef match is {:?}", m);
571 let msrc = session.load_source_file(&m.filepath);
572 let blobstart = m.point - BytePos(5); // 5 == "type ".len()
573 let blob = msrc.get_src_from_start(blobstart);
574 let type_ = blob.iter_stmts().nth(0).and_then(|range| {
575 let range = range.shift(blobstart);
576 let blob = msrc[range.to_range()].to_owned();
577 debug!("get_type_of_typedef blob string {}", blob);
578 let scope = Scope::new(m.filepath.clone(), range.start);
579 ast::parse_type(blob, &scope).type_
580 })?;
581 match type_.dereference() {
582 Ty::Match(m) => Some(m),
583 Ty::Ptr(_, _) => PrimKind::Pointer.to_module_match(),
584 Ty::Array(_, _) => PrimKind::Array.to_module_match(),
585 Ty::Slice(_) => PrimKind::Slice.to_module_match(),
586 Ty::PathSearch(paths) => {
587 let src = session.load_source_file(&m.filepath);
588 let scope_start = scopes::scope_start(src.as_src(), m.point);
589 // Type of TypeDef cannot be inside the impl block so look outside
590 let outer_scope_start = scope_start
591 .0
592 .checked_sub(1)
593 .map(|sub| scopes::scope_start(src.as_src(), sub.into()))
594 .and_then(|s| {
595 let blob = src.get_src_from_start(s);
596 let blob = blob.trim_start();
597 if blob.starts_with("impl") || util::trim_visibility(blob).starts_with("trait")
598 {
599 Some(s)
600 } else {
601 None
602 }
603 });
604 nameres::resolve_path_with_primitive(
605 &paths.path,
606 &paths.filepath,
607 outer_scope_start.unwrap_or(scope_start),
608 core::SearchType::StartsWith,
609 core::Namespace::Type,
610 session,
611 )
612 .into_iter()
613 .filter(|m_| Some(m_.matchstr.as_ref()) == paths.path.name() && m_.point != m.point)
614 .next()
615 }
616 _ => None,
617 }
618 }
619
get_type_of_static(m: Match) -> Option<Ty>620 fn get_type_of_static(m: Match) -> Option<Ty> {
621 let Match {
622 filepath,
623 point,
624 contextstr,
625 ..
626 } = m;
627 let scope = Scope::new(filepath, point - "static".len().into());
628 let res = ast::parse_static(contextstr, scope);
629 res.ty
630 }
631